UFO: Alien Invasion
g_ai_lua.cpp
Go to the documentation of this file.
1
15/*
16Copyright (C) 2002-2022 UFO: Alien Invasion.
17
18This program is free software; you can redistribute it and/or
19modify it under the terms of the GNU General Public License
20as published by the Free Software Foundation; either version 2
21of the License, or (at your option) any later version.
22
23This program is distributed in the hope that it will be useful,
24but WITHOUT ANY WARRANTY; without even the implied warranty of
25MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
26
27See the GNU General Public License for more details.
28
29You should have received a copy of the GNU General Public License
30along with this program; if not, write to the Free Software
31Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
32
33*/
34
35#include "../shared/cxx.h"
36
37#include "g_local.h"
38#include "g_ai.h"
39#include "g_actor.h"
40#include "g_client.h"
41#include "g_combat.h"
42#include "g_edicts.h"
43#include "g_health.h"
44#include "g_move.h"
45#include "g_utils.h"
46#include "g_vis.h"
47extern "C" {
48#include <lauxlib.h>
49}
50
51#define POS3_METATABLE "pos3"
52#define ACTOR_METATABLE "actor"
53#define AI_METATABLE "ai"
55static lua_State* ailState;
59#define luaL_dobuffer(L, b, n, s) \
60 (luaL_loadbuffer(L, b, n, s) || lua_pcall(L, 0, LUA_MULTRET, 0))
61#define AIL_invalidparameter(n) \
62 gi.DPrintf("AIL: Invalid parameter #%d in '%s'.\n", n, __func__)
63
65typedef enum {
66 AILVT_ALL, /* Don't do vis checks (god's view) */
67 AILVT_SIGHT, /* Standard vis check */
68 AILVT_TEAM, /* Team vis check */
69 AILVT_DIST /* Check only vis distance */
71
73typedef enum {
74 AILSC_DIST, /* Sort by line distance */
75 AILSC_PATH, /* Sort by pathing cost */
76 AILSC_HP /* Sort by HP */
78
80typedef enum {
81 AILSP_FAST, /* Fastest to get to */
82 AILSP_NEAR, /* Nearest to target */
83 AILSP_FAR, /* Farthest from target (within weapon's range) */
84 AILSP_DMG /* Best expected damage */
86
87typedef enum {
88 AILPW_RAND, /* Wander randomly */
89 AILPW_CW, /* Move clockwise */
90 AILPW_CCW /* Move counter-clockwise */
92/*
93 * Helper functions
94 */
95
102static const char* AIL_toTeamString (const int team)
103{
104 const char* teamStr = gi.GetConstVariable("luaaiteam", team);
105 if (teamStr == nullptr)
107 return teamStr;
108}
109
117static int AIL_toTeamInt (const char* team, const int param)
118{
119 int teamInt = TEAM_DEFAULT;
120 if (!gi.GetConstIntFromNamespace("luaaiteam", team, &teamInt))
122 return teamInt;
123}
124
131static ailVisType_t AIL_toVisInt (lua_State* L, const int index)
132{
133 int visInt = AILVT_ALL;
134 if (lua_isstring(L, index)) {
135 const char* s = lua_tostring(L, index);
136 if (!gi.GetConstIntFromNamespace("luaaivis", s, &visInt))
138 } else
140 return static_cast<ailVisType_t> (visInt);
141}
142
149static ailSortCritType_t AIL_toSortInt (lua_State* L, const int index)
150{
151 int sortInt = AILSC_DIST;
152 if (lua_isstring(L, index)) {
153 const char* s = lua_tostring(L, index);
154 if (!gi.GetConstIntFromNamespace("luaaisort", s, &sortInt))
156 } else
158 return static_cast<ailSortCritType_t> (sortInt);
159}
160
167static ailSortCritType_t AIL_toDistInt (lua_State* L, const int index)
168{
169 int distInt = AILSC_DIST;
170 if (lua_isstring(L, index)) {
171 const char* s = lua_tostring(L, index);
172 if (!gi.GetConstIntFromNamespace("luaaidist", s, &distInt))
174 } else
176 return static_cast<ailSortCritType_t> (distInt);
177}
178
185static ailShootPosType_t AIL_toShotPInt (lua_State* L, const int index)
186{
187 int spInt = AILSP_FAST;
188 if (lua_isstring(L, index)) {
189 const char* s = lua_tostring(L, index);
190 if (!gi.GetConstIntFromNamespace("luaaishot", s, &spInt))
192 } else
194 return static_cast<ailShootPosType_t> (spInt);
195}
196
203static ailWanderPosType AIL_toWanderPInt (lua_State* L, const int index)
204{
205 int wpInt = AILPW_RAND;
206 if (lua_isstring(L, index)) {
207 const char* s = lua_tostring(L, index);
208 if (!gi.GetConstIntFromNamespace("luaaiwander", s, &wpInt))
210 } else
212 return static_cast<ailWanderPosType> (wpInt);
213}
214
218typedef struct aiActor_s {
220} aiActor_t;
221
222
223/* Table sorting */
224template<typename T>
228};
229
230template<typename T>
232 return (i.sortLookup < j.sortLookup);
233}
234
235/*
236 * Current AI Actor.
237 */
238static Actor* AIL_ent;
239static Player* AIL_player;
241static float AIL_GetBestShot(const Actor& shooter, const Actor& target, const int tu, const float dist, shoot_types_t& bestType, fireDefIndex_t& bestFd, int& bestShots)
242{
243 int shotChecked = NONE;
244 float bestDmg = 0.0f;
245 bestShots = bestType = bestFd = NONE;
246 for (shoot_types_t shootType = ST_RIGHT; shootType < ST_NUM_SHOOT_TYPES; shootType++) {
247 const Item* item = AI_GetItemForShootType(shootType, AIL_ent);
248 if (item == nullptr)
249 continue;
250
251 const fireDef_t* fdArray = item->getFiredefs();
252 if (fdArray == nullptr)
253 continue;
254
255 for (fireDefIndex_t fdIdx = 0; fdIdx < item->ammoDef()->numFiredefs[fdArray->weapFdsIdx]; fdIdx++) {
256 const fireDef_t* fd = &fdArray[fdIdx];
257 const int time = G_ActorGetModifiedTimeForFiredef(AIL_ent, fd, false);
258 /* how many shoots can this actor do */
259 const int shots = tu / time;
260
261 if (!shots)
262 continue;
263
264 if (!AI_FighterCheckShoot(AIL_ent, &target, fd, dist))
265 continue;
266
267 const int shotFlags = fd->gravity | (fd->launched << 1) | (fd->rolled << 2);
268 if (shotChecked != shotFlags) {
269 shotChecked = shotFlags;
270 if (!AI_CheckLineOfFire(AIL_ent, &target, fd, shots))
271 continue;
272 }
273
274 /* Check if we can do the most damage here */
275 float dmg = AI_CalcShotDamage(AIL_ent, &target, fd, shootType) * shots;
276 if (dmg > bestDmg) {
277 bestDmg = dmg;
278 bestShots = shots;
279 bestFd = fdIdx;
280 bestType = shootType;
281 }
282 }
283 }
284
285 return bestDmg;
286}
287
288/*
289 * Actor metatable.
290 */
291/* Internal functions. */
292static int actorL_register(lua_State* L);
293static int lua_isactor(lua_State* L, int index);
294static aiActor_t* lua_toactor(lua_State* L, int index);
295static aiActor_t* lua_pushactor(lua_State* L, aiActor_t* actor);
296/* Metatable functions. */
297static int actorL_tostring(lua_State* L);
298static int actorL_pos(lua_State* L);
299static int actorL_shoot(lua_State* L);
300static int actorL_team(lua_State* L);
301static int actorL_throwgrenade(lua_State* L);
302static int actorL_TU(lua_State* L);
303static int actorL_HP(lua_State* L);
304static int actorL_morale(lua_State* L);
305static int actorL_isinjured(lua_State* L);
306static int actorL_isarmed(lua_State* L);
307static int actorL_isdead(lua_State* L);
308static int actorL_isvalidtarget(lua_State* L);
312static const luaL_reg actorL_methods[] = {
313 {"__tostring", actorL_tostring},
314 {"pos", actorL_pos},
315 {"shoot", actorL_shoot},
316 {"team", actorL_team},
317 {"throwgrenade", actorL_throwgrenade},
318 {"TU", actorL_TU},
319 {"HP", actorL_HP},
320 {"morale", actorL_morale},
321 {"isinjured", actorL_isinjured},
322 {"isarmed", actorL_isarmed},
323 {"isdead", actorL_isdead},
324 {"isvalidtarget", actorL_isvalidtarget},
325 {nullptr, nullptr}
326};
327
331/* Internal functions. */
332static int pos3L_register(lua_State* L);
333static int lua_ispos3(lua_State* L, int index);
334static pos3_t* lua_topos3(lua_State* L, int index);
335static pos3_t* lua_pushpos3(lua_State* L, pos3_t* pos);
336/* Metatable functions. */
337static int pos3L_tostring(lua_State* L);
338static int pos3L_goto(lua_State* L);
339static int pos3L_face(lua_State* L);
340static int pos3L_distance(lua_State* L);
344static const luaL_reg pos3L_methods[] = {
345 {"__tostring", pos3L_tostring},
346 {"goto", pos3L_goto},
347 {"face", pos3L_face},
348 {"distance", pos3L_distance},
349 {nullptr, nullptr}
350};
351
352
356static int AIL_print(lua_State* L);
357static int AIL_squad(lua_State* L);
358static int AIL_select(lua_State* L);
359static int AIL_see(lua_State* L);
360static int AIL_crouch(lua_State* L);
361static int AIL_reactionfire(lua_State* L);
362static int AIL_roundsleft(lua_State* L);
363static int AIL_canreload(lua_State* L);
364static int AIL_reload(lua_State* L);
365static int AIL_positionshoot(lua_State* L);
366static int AIL_positionhide(lua_State* L);
367static int AIL_positionherd(lua_State* L);
368static int AIL_positionapproach(lua_State* L);
369static int AIL_grabweapon(lua_State* L);
370static int AIL_missiontargets(lua_State* L);
371static int AIL_waypoints(lua_State* L);
372static int AIL_positionmission(lua_State* L);
373static int AIL_positionwander(lua_State* L);
374static int AIL_findweapons(lua_State* L);
375static int AIL_isfighter(lua_State* L);
376static int AIL_setwaypoint(lua_State* L);
377static int AIL_difficulty(lua_State* L);
378static int AIL_positionflee(lua_State* L);
379static int AIL_weapontype(lua_State* L);
380static int AIL_actor(lua_State* L);
381static int AIL_tusforshooting(lua_State* L);
382static int AIL_class(lua_State* L);
383static int AIL_hideneeded(lua_State* L);
384
388static const luaL_reg AIL_methods[] = {
389 {"print", AIL_print},
390 {"squad", AIL_squad},
391 {"select", AIL_select},
392 {"see", AIL_see},
393 {"crouch", AIL_crouch},
394 {"reactionfire", AIL_reactionfire},
395 {"roundsleft", AIL_roundsleft},
396 {"canreload", AIL_canreload},
397 {"reload", AIL_reload},
398 {"positionshoot", AIL_positionshoot},
399 {"positionhide", AIL_positionhide},
400 {"positionherd", AIL_positionherd},
401 {"positionapproach", AIL_positionapproach},
402 {"grabweapon", AIL_grabweapon},
403 {"missiontargets", AIL_missiontargets},
404 {"waypoints", AIL_waypoints},
405 {"positionmission", AIL_positionmission},
406 {"positionwander", AIL_positionwander},
407 {"findweapons", AIL_findweapons},
408 {"isfighter", AIL_isfighter},
409 {"setwaypoint", AIL_setwaypoint},
410 {"difficulty", AIL_difficulty},
411 {"positionflee", AIL_positionflee},
412 {"weapontype", AIL_weapontype},
413 {"actor", AIL_actor},
414 {"tusforshooting", AIL_tusforshooting},
415 {"class", AIL_class},
416 {"hideneeded", AIL_hideneeded},
417 {nullptr, nullptr}
418};
419
420
430static int actorL_register (lua_State* L)
431{
432 /* Create the metatable */
433 luaL_newmetatable(L, ACTOR_METATABLE);
434
435 /* Create the access table */
436 lua_pushvalue(L, -1);
437 lua_setfield(L, -2, "__index");
438
439 /* Register the values */
440 luaL_register(L, nullptr, actorL_methods);
441
442 /* Clean up stack. */
443 lua_pop(L, 1);
444
445 return 0; /* No error */
446}
447
454static int lua_isactor (lua_State* L, int index)
455{
456 if (lua_getmetatable(L, index) == 0)
457 return 0;
458 lua_getfield(L, LUA_REGISTRYINDEX, ACTOR_METATABLE);
459
460 int ret = 0;
461 if (lua_rawequal(L, -1, -2)) /* does it have the correct metatable? */
462 ret = 1;
463
464 lua_pop(L, 2); /* remove both metatables */
465 return ret;
466}
467
471static aiActor_t* lua_toactor (lua_State* L, int index)
472{
473 if (lua_isactor(L, index)) {
474 return (aiActor_t*) lua_touserdata(L, index);
475 }
476 luaL_typerror(L, index, ACTOR_METATABLE);
477 return nullptr;
478}
479
483static aiActor_t* lua_pushactor (lua_State* L, aiActor_t* actor)
484{
485 aiActor_t* a = (aiActor_t*) lua_newuserdata(L, sizeof(aiActor_t));
486 *a = *actor;
487 luaL_getmetatable(L, ACTOR_METATABLE);
488 lua_setmetatable(L, -2);
489 return a;
490}
491
495static int actorL_tostring (lua_State* L)
496{
497 char buf[MAX_VAR];
498
499 assert(lua_isactor(L, 1));
500
501 const aiActor_t* target = lua_toactor(L, 1);
502 Com_sprintf(buf, sizeof(buf), "Actor( %s )", target->actor->chr.name);
503
504 lua_pushstring(L, buf);
505 return 1;
506}
507
511static int actorL_pos (lua_State* L)
512{
513 assert(lua_isactor(L, 1));
514
515 const aiActor_t* target = lua_toactor(L, 1);
516 lua_pushpos3(L, &target->actor->pos);
517 return 1;
518}
519
523static int actorL_shoot (lua_State* L)
524{
525 assert(lua_isactor(L, 1));
526
527 /* Target */
528 const aiActor_t* target = lua_toactor(L, 1);
529
530 /* Number of TU to spend shooting, fire mode will adjust to that. */
531 int tu = AIL_ent->getUsableTUs();
532 if (lua_gettop(L) > 1) {
533 assert(lua_isnumber(L, 2)); /* Must be a number. */
534
535 tu = std::min(static_cast<int>(lua_tonumber(L, 2)), tu);
536 }
537
538 const float dist = VectorDist(AIL_ent->origin, target->actor->origin);
539 shoot_types_t bestType = NONE;
540 fireDefIndex_t bestFd = NONE;
541 int bestShots = 0;
542 AIL_GetBestShot(*AIL_ent, *target->actor, tu, dist, bestType, bestFd, bestShots);
543
544 /* Failure - no weapon. */
545 if (bestType == NONE) {
546 lua_pushboolean(L, 0);
547 return 1;
548 }
549
550 bool shot = false;
551 while (bestShots > 0) {
552 if (G_IsDead(target->actor))
553 break;
554 bestShots--;
555 shot = G_ClientShoot(*AIL_player, AIL_ent, target->actor->pos, bestType, bestFd, nullptr, true, 0) || shot;
556 }
557
558 /* Success? */
559 lua_pushboolean(L, shot);
560 return 1;
561}
562
566static int actorL_team (lua_State* L)
567{
568 assert(lua_isactor(L, 1));
569
570 const aiActor_t* target = lua_toactor(L, 1);
571 assert(target != nullptr);
572 const char* team = AIL_toTeamString(target->actor->getTeam());
573 lua_pushstring(L, team);
574 return 1;
575}
576
580static int actorL_throwgrenade(lua_State* L)
581{
582 /* check parameter */
583 if (!(lua_gettop(L) && lua_isactor(L, 1))) {
585 lua_pushboolean(L, 0);
586 return 1;
587 }
588 const aiActor_t* target = lua_toactor(L, 1);
589 assert(target != nullptr);
590
591 /* Min number of enemies to use grenade */
592 int minNum = 0;
593 if (lua_gettop(L) > 1) {
594 if (!lua_isnumber(L, 2)) { /* Must be a number. */
596 lua_pushboolean(L, 0);
597 return 1;
598 }
599 minNum = static_cast<int>(lua_tonumber(L, 2));
600 }
601
602 /* Number of TU to spend */
603 int tus = AIL_ent->getUsableTUs();
604 if (lua_gettop(L) > 2) {
605 if (!lua_isnumber(L, 3)) { /* Must be a number. */
607 lua_pushboolean(L, 0);
608 return 1;
609 }
610 tus = std::min(static_cast<int>(lua_tonumber(L, 3)), tus);
611 }
612
613 /* Check that we have a free hand */
615 const Item* right = AIL_ent->getRightHandItem();
616 if (right)
617 hand = right->isHeldTwoHanded() || AIL_ent->getLeftHandItem() ? CID_MAX : CID_LEFT;
618 if (hand >= CID_MAX) {
619 lua_pushboolean(L, 0);
620 return 1;
621 }
622
623 /* Check if we have a grenade */
624 Item* grenade = nullptr;
625 const invDef_t* fromCont = AI_SearchGrenade(AIL_ent, &grenade);
626 if (!fromCont || !grenade) {
627 lua_pushboolean(L, 0);
628 return 1;
629 }
630 /* Now check if we can use it */
631 const fireDef_t* fdArray = grenade->getFiredefs();
632 const int invMoveCost = fromCont->out + INVDEF(hand)->in;
633 const shoot_types_t shotType = hand == CID_RIGHT ? ST_RIGHT : ST_LEFT;
634 float dist = VectorDist(AIL_ent->origin, target->actor->origin);
635 const fireDef_t* bestFd = nullptr;
636 for (fireDefIndex_t fdIdx = 0; fdIdx < grenade->ammoDef()->numFiredefs[fdArray->weapFdsIdx]; fdIdx++) {
637 const fireDef_t* fd = &fdArray[fdIdx];
638 const int time = invMoveCost + G_ActorGetModifiedTimeForFiredef(AIL_ent, fd, false);
639 /* Enough TU? */
640 if (time > tus)
641 continue;
642 /* In range? */
643 if (!AI_FighterCheckShoot(AIL_ent, target->actor, fd, dist))
644 continue;
645 /* LOF? */
646 if (!AI_CheckLineOfFire(AIL_ent, target->actor, fd, 1))
647 continue;
648
649 /* Select the first usable firemode */
650 bestFd = fd;
651 break;
652 }
653 if (!bestFd) {
654 lua_pushboolean(L, 0);
655 return 1;
656 }
657
658 /* Finally check if we want to use it now */
659 if (bestFd->splrad > 0) {
660 Actor* check = nullptr;
661 int n = 0;
662 while ((check = G_EdictsGetNextLivingActor(check))) {
663 /* check for distance */
664 dist = VectorDist(target->actor->origin, check->origin);
665 dist = dist > UNIT_SIZE / 2 ? dist - UNIT_SIZE / 2 : 0;
666 if (dist > bestFd->splrad)
667 continue;
668
669 if (!AI_IsHostile(AIL_ent, target->actor)) {
670 lua_pushboolean(L, 0);
671 return 1;
672 }
673 ++n;
674 }
675 /* Check there's large enough group of targets */
676 if (n < minNum) {
677 lua_pushboolean(L, 0);
678 return 1;
679 }
680 }
681
682 /* Try to move the grenade to the free hand */
683 if(!G_ActorInvMove(AIL_ent, fromCont, grenade, INVDEF(hand), NONE, NONE, true)) {
684 lua_pushboolean(L, 0);
685 return 1;
686 }
687 /* All right use it! */
688 const bool result = G_ClientShoot(*AIL_player, AIL_ent, target->actor->pos, shotType, bestFd->fdIdx, nullptr, true, 0);
689
690 lua_pushboolean(L, result);
691 return 1;
692}
693
697static int actorL_TU (lua_State* L)
698{
699 /* check parameter */
700 if (!(lua_gettop(L) && lua_isactor(L, 1))) {
702 lua_pushboolean(L, 0);
703 return 1;
704 }
705 const aiActor_t* actor = lua_toactor(L, 1);
706 assert(actor != nullptr);
707
708 lua_pushnumber(L, actor->actor->getUsableTUs());
709 return 1;
710}
711
715static int actorL_HP (lua_State* L)
716{
717 /* check parameter */
718 if (!(lua_gettop(L) && lua_isactor(L, 1))) {
720 lua_pushboolean(L, 0);
721 return 1;
722 }
723 const aiActor_t* actor = lua_toactor(L, 1);
724 assert(actor != nullptr);
725
726 lua_pushnumber(L, actor->actor->HP);
727 return 1;
728}
729
733static int actorL_morale (lua_State* L)
734{
735 /* check parameter */
736 if (!(lua_gettop(L) && lua_isactor(L, 1))) {
738 lua_pushboolean(L, 0);
739 return 1;
740 }
741 const aiActor_t* actor = lua_toactor(L, 1);
742 assert(actor != nullptr);
743
744 const char* morStat = "normal";
745 if (actor->actor->isPanicked())
746 morStat = "panic";
747 else if (actor->actor->isInsane())
748 morStat = "insane";
749 else if (actor->actor->isRaged())
750 morStat = "rage";
751 else if (actor->actor->getMorale() <= mor_brave->integer)
752 morStat = "cower";
753
754 lua_pushstring(L, morStat);
755 return 1;
756}
757
761static int actorL_isinjured (lua_State* L)
762{
763 /* check parameter */
764 if (!(lua_gettop(L) && lua_isactor(L, 1))) {
766 lua_pushboolean(L, 0);
767 return 1;
768 }
769 const aiActor_t* actor = lua_toactor(L, 1);
770 assert(actor != nullptr);
771
772 lua_pushboolean(L, G_IsActorWounded(actor->actor, true)
773 || actor->actor->HP <= actor->actor->chr.maxHP * 0.5);
774 return 1;
775}
776
780static int actorL_isarmed (lua_State* L)
781{
782 /* check parameter */
783 if (!(lua_gettop(L) && lua_isactor(L, 1))) {
785 lua_pushboolean(L, 0);
786 return 1;
787 }
788 const aiActor_t* actor = lua_toactor(L, 1);
789 assert(actor != nullptr);
790
791 lua_pushboolean(L, actor->actor->getRightHandItem() ? 1 : 0);
792 lua_pushboolean(L, actor->actor->getLeftHandItem() ? 1 : 0);
793 return 2;
794}
795
799static int actorL_isdead (lua_State* L)
800{
801 /* check parameter */
802 if (!(lua_gettop(L) && lua_isactor(L, 1))) {
804 lua_pushboolean(L, 0);
805 return 1;
806 }
807 const aiActor_t* actor = lua_toactor(L, 1);
808 assert(actor != nullptr);
809
810 lua_pushboolean(L, actor->actor->isDead());
811 return 1;
812}
813
817static int actorL_isvalidtarget (lua_State* L)
818{
819 assert(lua_isactor(L, 1));
820
821 const aiActor_t* target = lua_toactor(L, 1);
822 assert(target != nullptr);
823 lua_pushboolean(L, AI_IsHostile(AIL_ent, target->actor));
824 return 1;
825}
826
827
837static int pos3L_register (lua_State* L)
838{
839 /* Create the metatable */
840 luaL_newmetatable(L, POS3_METATABLE);
841
842 /* Create the access table */
843 lua_pushvalue(L, -1);
844 lua_setfield(L, -2, "__index");
845
846 /* Register the values */
847 luaL_register(L, nullptr, pos3L_methods);
848
849 /* Clean up the stack. */
850 lua_pop(L, 1);
851
852 return 0; /* No error */
853}
854
861static int lua_ispos3 (lua_State* L, int index)
862{
863 if (lua_getmetatable(L, index) == 0)
864 return 0;
865 lua_getfield(L, LUA_REGISTRYINDEX, POS3_METATABLE);
866
867 int ret = 0;
868 if (lua_rawequal(L, -1, -2)) /* does it have the correct metatable? */
869 ret = 1;
870
871 lua_pop(L, 2); /* remove both metatables */
872 return ret;
873}
874
878static pos3_t* lua_topos3 (lua_State* L, int index)
879{
880 if (lua_ispos3(L, index)) {
881 return (pos3_t*) lua_touserdata(L, index);
882 }
883 luaL_typerror(L, index, POS3_METATABLE);
884 return nullptr;
885}
886
890static pos3_t* lua_pushpos3 (lua_State* L, pos3_t* pos)
891{
892 pos3_t* p = (pos3_t*) lua_newuserdata(L, sizeof(pos3_t));
893 memcpy(p, pos, sizeof(*p));
894 luaL_getmetatable(L, POS3_METATABLE);
895 lua_setmetatable(L, -2);
896 return p;
897}
898
902static int pos3L_tostring (lua_State* L)
903{
904 char buf[MAX_VAR];
905
906 assert(lua_ispos3(L, 1));
907
908 const pos3_t* p = lua_topos3(L, 1);
909 Com_sprintf(buf, sizeof(buf), "Pos3( x=%d, y=%d, z=%d )", (*p)[0], (*p)[1], (*p)[2]);
910
911 lua_pushstring(L, buf);
912 return 1;
913}
914
918static int pos3L_goto (lua_State* L)
919{
920 assert(lua_ispos3(L, 1));
921
922 /* Calculate move table. */
924 gi.MoveStore(level.pathingMap);
925
926 /* Move. */
927 const pos3_t* pos = lua_topos3(L, 1);
928 /* do the move */
929 for (;;) {
930 if (AIL_ent->isDead())
931 break;
932 G_ClientMove(*AIL_player, 0, AIL_ent, *pos);
933 if (AIL_ent->isSamePosAs(*pos))
934 break;
935 const pos_t length = G_ActorMoveLength(AIL_ent, level.pathingMap, *pos, false);
937 break;
938 }
939
940 lua_pushboolean(L, AIL_ent->isSamePosAs(*pos));
941 return 1;
942}
943
947static int pos3L_face (lua_State* L)
948{
949 assert(lua_ispos3(L, 1));
950
951 const pos3_t* pos = lua_topos3(L, 1);
953
954 lua_pushboolean(L, 1);
955 return 1;
956}
957
961static int pos3L_distance (lua_State* L)
962{
963 assert(lua_ispos3(L, 1));
964
965 pos3_t* pos = lua_topos3(L, 1);
966 assert(pos != nullptr);
967
968 ailSortCritType_t distType = AILSC_DIST;
969 if (lua_gettop(L) > 1)
970 distType = AIL_toDistInt(L, 2);
971
972 switch (distType) {
973 case AILSC_PATH:
974 /* Find a path to the target pos */
976 lua_pushnumber(L, ROUTING_NOT_REACHABLE);
977 return 1;
978 }
979 lua_pushnumber(L, G_ActorMoveLength(AIL_ent, level.pathingMap, *pos, false));
980 return 1;
981 case AILSC_DIST:
982 default:
983 vec3_t to;
984 PosToVec(*pos, to);
985 lua_pushnumber(L, VectorDist(AIL_ent->origin, to));
986 return 1;
987 }
988}
989
993/*
994 * General functions.
995 */
996
1000static int AIL_print (lua_State* L)
1001{
1002 const int n = lua_gettop(L); /* number of arguments */
1003
1004 for (int i = 1; i <= n; i++) {
1005 const char* s;
1006 bool meta = false;
1007
1008 lua_pushvalue(L, i); /* value to print */
1009 if (luaL_callmeta(L, -1, "__tostring")) {
1010 s = lua_tostring(L, -1);
1011 meta = true;
1012 } else {
1013 switch (lua_type(L, -1)) {
1014 case LUA_TNUMBER:
1015 case LUA_TSTRING:
1016 s = lua_tostring(L, -1);
1017 break;
1018 case LUA_TBOOLEAN:
1019 s = lua_toboolean(L, -1) ? "true" : "false";
1020 break;
1021 case LUA_TNIL:
1022 s = "nil";
1023 break;
1024
1025 default:
1026 s = "unknown lua type";
1027 break;
1028 }
1029 }
1030 gi.DPrintf("%s%s", (i > 1) ? "\t" : "", s);
1031 lua_pop(L, 1); /* Pop the value */
1032 if (meta) /* Meta creates an additional string. */
1033 lua_pop(L, 1);
1034 }
1035
1036 gi.DPrintf("\n");
1037 return 0;
1038}
1039
1040/*
1041 * Player functions.
1042 */
1043
1047static int AIL_squad (lua_State* L)
1048{
1049 if (g_ailua->integer < 2) {
1050 gi.DPrintf("Problem while running lua: attempt to get the player's team while not in team mode.");
1051 lua_pushnil(L);
1052 return 1;
1053 }
1054
1055 /* New Lua table. */
1056 lua_newtable(L);
1057
1058 int i = 1; /* LUA indexes starting from one */
1059 Actor* check = nullptr;
1060 while ((check = G_EdictsGetNextActor(check))) {
1061 if (check->getPlayerNum() != AIL_player->getNum())
1062 continue;
1063 lua_pushnumber(L, i++); /* index */
1064 aiActor_t target;
1065 target.actor = check;
1066 lua_pushactor(L, &target); /* value */
1067 lua_rawset(L, -3); /* store the value in the table */
1068 }
1069 return 1; /* Returns the table of actors. */
1070}
1071
1075static int AIL_select (lua_State* L)
1076{
1077 if (g_ailua->integer < 2) {
1078 gi.DPrintf("Problem while running lua: attempt to select the active AI actor while not in team mode.");
1079 lua_pushnil(L);
1080 } else if (lua_gettop(L) > 0 && lua_isactor(L, 1)) {
1081 aiActor_t* target = lua_toactor(L, 1);
1082 if (target->actor->getPlayerNum() == AIL_player->getNum())
1083 AIL_ent = target->actor;
1084 lua_pushboolean(L, AIL_ent == target->actor);
1085 } else {
1087 lua_pushboolean(L, false);
1088 }
1089 return 1;
1090}
1091
1092/*
1093 * Actor functions
1094 */
1095
1099static int AIL_see (lua_State* L)
1100{
1101 /* Defaults. */
1102 int team = TEAM_ALL;
1103 ailVisType_t vision = AILVT_ALL;
1104 ailSortCritType_t sortCrit = AILSC_DIST;
1105 bool invTeam = false;
1106
1107 /* Handle parameters. */
1108 if ((lua_gettop(L) > 0)) {
1109 /* Get what to "see" with. */
1110 vision = AIL_toVisInt(L, 1);
1111
1112 /* We now check for different teams. */
1113 if ((lua_gettop(L) > 1)) {
1114 if (lua_isstring(L, 2)) {
1115 const char* s = lua_tostring(L, 2);
1116 if (s[0] == '-' || s[0] == '~') {
1117 invTeam = true;
1118 ++s;
1119 }
1120 team = AIL_toTeamInt(s, 2);
1121 /* Trying to see no one? */
1122 if (team == TEAM_ALL && invTeam)
1124 } else
1126 }
1127
1128 /* Sorting criteria */
1129 if ((lua_gettop(L) > 2)) {
1130 sortCrit = AIL_toSortInt(L, 3);
1131 }
1132 }
1133
1134 int n = 0;
1135 Actor* check = nullptr;
1137 /* Get visible things. */
1138 const int visDist = G_VisCheckDist(AIL_ent);
1139 /* We are about to check the team view, update it accordingly */
1140 if (vision == AILVT_TEAM)
1142 while ((check = G_EdictsGetNextLivingActor(check))) {
1143 if (AIL_ent == check)
1144 continue;
1145 const float distance = VectorDistSqr(AIL_ent->pos, check->pos);
1146 /* Check for team match if needed. */
1147 if ((team == TEAM_ALL || (check->getTeam() == team ? !invTeam : invTeam))
1148 && (vision == AILVT_ALL
1149 || (vision == AILVT_SIGHT && G_Vis(AIL_ent->getTeam(), AIL_ent, check, VT_NOFRUSTUM))
1150 || (vision == AILVT_TEAM && G_IsVisibleForTeam(check, AIL_ent->getTeam()))
1151 || (vision == AILVT_DIST && distance <= visDist * visDist))) {
1152 switch (sortCrit) {
1153 case AILSC_PATH:
1154 {
1156 if (G_FindPath(0, AIL_ent, AIL_ent->pos, check->pos, false, 0xFE))
1157 move = gi.MoveLength(level.pathingMap, check->pos, 0, false);
1158 sortTable[n].sortLookup = move;
1159 }
1160 break;
1161 case AILSC_HP:
1162 sortTable[n].sortLookup = check->HP;
1163 break;
1164 case AILSC_DIST:
1165 default:
1166 sortTable[n].sortLookup = VectorDistSqr(AIL_ent->pos, check->pos);
1167 break;
1168 }
1169 sortTable[n++].data = check;
1170 }
1171 }
1172
1173 /* Sort by given criterion - lesser first. */
1174 std::sort(sortTable, sortTable + n);
1175
1176 /* Now save it in a Lua table. */
1177 lua_newtable(L);
1178 for (int i = 0; i < n; i++) {
1179 lua_pushnumber(L, i + 1); /* index, starts with 1 */
1180 aiActor_t target;
1181 target.actor = sortTable[i].data;
1182 lua_pushactor(L, &target); /* value */
1183 lua_rawset(L, -3); /* store the value in the table */
1184 }
1185 return 1; /* Returns the table of actors. */
1186}
1187
1191static int AIL_crouch (lua_State* L)
1192{
1193 if (lua_gettop(L) > 0) {
1194 if (lua_isboolean(L, 1)) {
1195 const bool reqState = lua_toboolean(L, 1);
1196 const bool state = AIL_ent->isCrouched();
1197 if (reqState != state)
1199 } else
1201 }
1202
1203 lua_pushboolean(L, AIL_ent->isCrouched());
1204 return 1;
1205}
1206
1210static int AIL_reactionfire (lua_State* L)
1211{
1212 if (lua_gettop(L) > 0) {
1213 int reactionState = 0;
1214
1215 if (lua_isstring(L, 1)) {
1216 /* get reaction fire mode */
1217 const char* cmd = lua_tostring(L, 1);
1218 reactionState = Q_streq(cmd, "disable") ? ~STATE_REACTION : STATE_REACTION;
1219 }
1220
1221 if (reactionState) {
1222 G_ClientStateChange(*AIL_player, AIL_ent, reactionState, true);
1223 } else {
1225 }
1226 }
1227
1228 lua_pushboolean(L, AIL_ent->isReaction());
1229 return 1;
1230}
1231
1235static int AIL_roundsleft (lua_State* L)
1236{
1237 /* Right hand */
1238 const Item* rightHand = AIL_ent->getRightHandItem();
1239 if (rightHand && (rightHand->def()->ammo < 1 || rightHand->getAmmoLeft() > 0))
1240 lua_pushnumber(L, rightHand->getAmmoLeft());
1241 else
1242 /* Currently unusable */
1243 lua_pushnil(L);
1244
1245 /* Left hand */
1246 const Item* leftHand = AIL_ent->getLeftHandItem();
1247 if (leftHand && (leftHand->def()->ammo < 1 || leftHand->getAmmoLeft() > 0))
1248 lua_pushnumber(L, leftHand->getAmmoLeft());
1249 else
1250 lua_pushnil(L);
1251 return 2;
1252}
1253
1257static int AIL_canreload (lua_State* L)
1258{
1259 lua_pushboolean(L, G_ClientCanReload(AIL_ent, CID_RIGHT));
1260 lua_pushboolean(L, G_ClientCanReload(AIL_ent, CID_LEFT));
1261 return 2;
1262}
1263
1267static int AIL_reload (lua_State* L)
1268{
1269 containerIndex_t container = CID_RIGHT; /* Default to right hand. */
1270
1271 if (lua_gettop(L) > 0) {
1272 if (lua_isstring(L, 1)) {
1273 const char* s = lua_tostring(L, 1);
1274
1275 if (Q_streq(s, "right")) {
1276 container = CID_RIGHT;
1277 } else if (Q_streq(s, "left")) {
1278 container = CID_LEFT;
1279 } else {
1281 return 0;
1282 }
1283 } else {
1285 return 0;
1286 }
1287 }
1288
1289 AI_TryToReloadWeapon(AIL_ent, container);
1290 return 0;
1291}
1292
1296static int AIL_grabweapon (lua_State* L)
1297{
1298 lua_pushboolean(L, G_ClientGetWeaponFromInventory(AIL_ent));
1299 return 1;
1300}
1301
1305static int AIL_positionshoot (lua_State* L)
1306{
1307 /* We need a target. */
1308 assert(lua_isactor(L, 1));
1309 aiActor_t* target = lua_toactor(L, 1);
1310
1311 /* Make things more simple. */
1312 Actor* actor = AIL_ent;
1313
1314 /* Shooting strategy */
1315 ailShootPosType_t posType = AILSP_FAST;
1316 if ((lua_gettop(L) > 1))
1317 posType = AIL_toShotPInt(L, 2);
1318
1319 /* Number of TU to spend shooting, to make sure we have enough tus to actually fire. */
1320 int tus = actor->getUsableTUs();
1321 if (lua_gettop(L) > 2) {
1322 assert(lua_isnumber(L, 3)); /* Must be a number. */
1323
1324 tus = std::min(static_cast<int>(lua_tonumber(L, 3)), tus);
1325 }
1326
1327 /* Don't shoot units under our control */
1328 if (!AI_IsHostile(actor, target->actor)) {
1329 lua_pushboolean(L, 0);
1330 return 1;
1331 }
1332
1333 shoot_types_t shootType = ST_RIGHT;
1334 const Item* item = AI_GetItemForShootType(shootType, AIL_ent);
1335 if (item == nullptr) {
1336 shootType = ST_LEFT;
1337 item = AI_GetItemForShootType(shootType, AIL_ent);
1338 }
1339
1340 /* Check for weapon. */
1341 if (item == nullptr) {
1342 lua_pushboolean(L, 0);
1343 return 1;
1344 }
1345 const fireDef_t* fd = item->getFastestFireDef();
1346 if (fd == nullptr) {
1347 lua_pushboolean(L, 0);
1348 return 1;
1349 }
1350
1351 int fdTime = G_ActorGetModifiedTimeForFiredef(AIL_ent, fd, false);
1352 if (tus - fdTime <= 0) {
1353 lua_pushboolean(L, 0);
1354 return 1;
1355 }
1356
1357 /* Calculate move table. */
1358 G_MoveCalc(0, actor, actor->pos, tus);
1359 gi.MoveStore(level.pathingMap);
1360
1361 /* set borders */
1362 const int rad = (tus + 1) / TU_MOVE_STRAIGHT;
1363
1364 pos3_t oldPos;
1365 vec3_t oldOrigin;
1366 VectorCopy(actor->pos, oldPos);
1367 VectorCopy(actor->origin, oldOrigin);
1368
1369 /* evaluate moving to every possible location in the search area,
1370 * including combat considerations */
1371 float bestScore = 0.0f;
1372 pos3_t to, bestPos;
1373 VectorSet(bestPos, 0, 0, PATHFINDING_HEIGHT);
1374 AiAreaSearch searchArea(oldPos, rad);
1375 while (searchArea.getNext(to)) {
1376 actor->setOrigin(to);
1377 const pos_t move = G_ActorMoveLength(actor, level.pathingMap, to, true);
1378 if (move > tus || move == ROUTING_NOT_REACHABLE)
1379 continue;
1380 if (!AI_CheckPosition(actor, actor->pos))
1381 continue;
1382 /* Can we see the target? */
1383 if (!G_IsVisibleForTeam(target->actor, actor->getTeam()) && G_ActorVis(actor, target->actor, true) < ACTOR_VIS_10)
1384 continue;
1385
1386 const float dist = VectorDist(actor->origin, target->actor->origin);
1387 int dummy = NONE;
1388 const float bestDmg = AIL_GetBestShot(*actor, *target->actor, tus - move, dist, dummy, dummy, dummy);
1389 if (dummy == NONE)
1390 continue;
1391
1392 float score;
1393 switch (posType) {
1394 case AILSP_NEAR:
1395 score = -dist;
1396 break;
1397 case AILSP_FAR:
1398 score = dist;
1399 break;
1400 case AILSP_DMG:
1401 score = bestDmg;
1402 break;
1403 case AILSP_FAST:
1404 default:
1405 score = -move;
1406 break;
1407 }
1408 if (score > bestScore || bestPos[2] >= PATHFINDING_HEIGHT) {
1409 VectorCopy(to, bestPos);
1410 bestScore = score;
1411 }
1412 }
1413
1414 VectorCopy(oldPos, actor->pos);
1415 VectorCopy(oldOrigin, actor->origin);
1416
1417 /* No position found in range. */
1418 if (bestPos[2] >= PATHFINDING_HEIGHT) {
1419 lua_pushboolean(L, 0);
1420 return 1;
1421 }
1422
1423 /* Return the spot. */
1424 lua_pushpos3(L, &bestPos);
1425 return 1;
1426}
1427
1433static int AIL_positionhide (lua_State* L)
1434{
1435 int hidingTeam = AI_GetHidingTeam(AIL_ent);
1436
1437 /* parse parameter */
1438 if (lua_gettop(L)) {
1439 if (lua_isstring(L, 1)) {
1440 const char* s = lua_tostring(L, 1);
1441 bool invTeam = false;
1442 if (s[0] == '-' || s[0] == '~') {
1443 invTeam = true;
1444 ++s;
1445 }
1446 const int team = AIL_toTeamInt(s, 1);
1447 if (team == TEAM_ALL)
1449 else if (invTeam)
1450 hidingTeam = -team;
1451 else
1452 hidingTeam = team;
1453 } else {
1455 }
1456 }
1457
1458 int tus = AIL_ent->getUsableTUs();
1459 /* parse parameter */
1460 if (lua_gettop(L) > 1) {
1461 if (lua_isnumber(L, 2)) {
1462 tus = std::min(static_cast<int>(lua_tonumber(L, 2)), tus);
1463 } else {
1465 }
1466 }
1467
1468 pos3_t save;
1469 VectorCopy(AIL_ent->pos, save);
1470
1471 if (AI_FindHidingLocation(hidingTeam, AIL_ent, AIL_ent->pos, tus)) {
1472 /* Return the spot. */
1473 lua_pushpos3(L, &AIL_ent->pos);
1474 } else {
1475 lua_pushboolean(L, 0);
1476 }
1477 AIL_ent->setOrigin(save);
1478 return 1;
1479}
1480
1489static int AIL_positionherd (lua_State* L)
1490{
1491 /* check parameter */
1492 if (!(lua_gettop(L) && lua_isactor(L, 1))) {
1494 lua_pushboolean(L, 0);
1495 return 1;
1496 }
1497 const aiActor_t* target = lua_toactor(L, 1);
1498
1499 int tus = AIL_ent->getUsableTUs();
1500 /* parse parameter */
1501 if (lua_gettop(L) > 1) {
1502 if (lua_isnumber(L, 2)) {
1503 tus = std::min(static_cast<int>(lua_tonumber(L, 2)), tus);
1504 } else {
1506 }
1507 }
1508
1509 bool inverse = false;
1510 if (lua_gettop(L) > 2) {
1511 if (lua_isboolean(L, 3))
1512 inverse = lua_toboolean(L, 3);
1513 else
1515 }
1516
1517 pos3_t save;
1518 VectorCopy(AIL_ent->pos, save);
1519 if (AI_FindHerdLocation(AIL_ent, AIL_ent->pos, target->actor->origin, tus, inverse)) {
1520 lua_pushpos3(L, &AIL_ent->pos);
1521 } else {
1522 lua_pushboolean(L, 0);
1523 }
1524 AIL_ent->setOrigin(save);
1525 return 1;
1526}
1527
1531static int AIL_positionapproach (lua_State* L)
1532{
1533 /* check parameter */
1534 if (!(lua_gettop(L) && lua_ispos3(L, 1))) {
1536 lua_pushboolean(L, 0);
1537 return 1;
1538 }
1539
1540 const pos3_t* target = lua_topos3(L, 1);
1541 assert(target != nullptr);
1542
1543 int tus = AIL_ent->getUsableTUs();
1544 if (lua_gettop(L) > 1) {
1545 if (lua_isnumber(L, 2))
1546 tus = std::min(static_cast<int>(lua_tonumber(L, 2)), tus);
1547 else
1549 }
1550
1551 bool hide = false;
1552 if (lua_gettop(L) > 2){
1553 if (lua_isboolean(L, 3))
1554 hide = lua_toboolean(L, 3);
1555 else
1557 }
1558
1559 /* Find a path to the target actor */
1560 const int maxTUs = ROUTING_NOT_REACHABLE - 1;
1561 pos3_t to;
1562 VectorCopy(*target, to);
1563 byte crouchingState = AIL_ent->isCrouched() ? 1 : 0;
1564 if (!G_FindPath(0, AIL_ent, AIL_ent->pos, to, crouchingState, maxTUs)) {
1565 /* Not found */
1566 lua_pushboolean(L, 0);
1567 return 1;
1568 }
1569
1570 /* Find the farthest we can go with current TUs */
1571 int dvec;
1572 while ((dvec = gi.MoveNext(level.pathingMap, to, crouchingState)) != ROUTING_UNREACHABLE) {
1573 /* Note: here we skip the first position so we don't try to walk into the target */
1574 PosSubDV(to, crouchingState, dvec);
1576 continue;
1577 if (!AI_CheckPosition(AIL_ent, to))
1578 continue;
1579 const byte length = G_ActorMoveLength(AIL_ent, level.pathingMap, to, false);
1580 if (length <= tus)
1581 break;
1582 /* We are going backwards to the origin. */
1583 }
1584
1585 if (AIL_ent->isSamePosAs(to))
1586 lua_pushboolean(L, 0);
1587 else
1588 lua_pushpos3(L, &to);
1589 return 1;
1590}
1591
1595static int AIL_missiontargets (lua_State* L)
1596{
1597
1598 /* Defaults. */
1599 int team = TEAM_ALL;
1600 ailVisType_t vision = AILVT_ALL;
1601 ailSortCritType_t sortCrit = AILSC_DIST;
1602 bool invTeam = false;
1603
1604 /* Handle parameters. */
1605 if ((lua_gettop(L) > 0)) {
1606 /* Get what to "see" with. */
1607 vision = AIL_toVisInt(L, 1);
1608
1609 /* We now check for different teams. */
1610 if ((lua_gettop(L) > 1)) {
1611 if (lua_isstring(L, 2)) {
1612 const char* s = lua_tostring(L, 2);
1613 if (s[0] == '-' || s[0] == '~') {
1614 invTeam = true;
1615 ++s;
1616 }
1617 team = AIL_toTeamInt(s, 2);
1618 /* Trying to see no one? */
1619 if (team == TEAM_ALL && invTeam)
1621 } else
1623 }
1624
1625 /* Sorting criteria */
1626 if ((lua_gettop(L) > 2))
1627 sortCrit = AIL_toDistInt(L, 3);
1628 }
1629
1630 int n = 0;
1632 /* Get visible things. */
1633 const int visDist = G_VisCheckDist(AIL_ent);
1634 Edict* mission = nullptr;
1635 while ((mission = G_EdictsGetNextInUse(mission))) {
1636 if (mission->type != ET_MISSION)
1637 continue;
1638 const float distance = VectorDistSqr(AIL_ent->pos, mission->pos);
1639 /* Check for team match if needed. */
1640 if ((team == TEAM_ALL || (mission->getTeam() == team ? !invTeam : invTeam))
1641 && (vision == AILVT_ALL
1642 || (vision == AILVT_SIGHT && !G_TestLineWithEnts(AIL_ent->origin, mission->origin))
1643 || (vision == AILVT_DIST && distance <= visDist * visDist))) {
1644 switch (sortCrit) {
1645 case AILSC_PATH:
1646 {
1648 if (G_FindPath(0, AIL_ent, AIL_ent->pos, mission->pos, false, ROUTING_NOT_REACHABLE - 1))
1649 move = gi.MoveLength(level.pathingMap, mission->pos, 0, false);
1650 sortTable[n].sortLookup = move;
1651 }
1652 break;
1653 case AILSC_DIST:
1654 default:
1655 sortTable[n].sortLookup = VectorDistSqr(AIL_ent->pos, mission->pos);
1656 break;
1657 }
1658 sortTable[n++].data = mission;
1659 }
1660 }
1661
1662 /* Sort by given criterion - lesser first. */
1663 std::sort(sortTable, sortTable + n);
1664
1665 /* Now save it in a Lua table. */
1666 lua_newtable(L);
1667 for (int i = 0; i < n; i++) {
1668 lua_pushnumber(L, i + 1); /* index, starts with 1 */
1669 lua_pushpos3(L, &sortTable[i].data->pos); /* value */
1670 lua_rawset(L, -3); /* store the value in the table */
1671 }
1672 return 1; /* Returns the table of positions. */
1673}
1674
1678static int AIL_waypoints (lua_State* L)
1679{
1680 const float minEnemyDist = 160.0f;
1681 /* Min distance to waypoint */
1682 float minDist = 800.0f;
1683 if (lua_gettop(L) > 0) {
1684 if (lua_isnumber(L, 1))
1685 minDist = lua_tonumber(L, 1);
1686 else
1688 }
1689
1690 /* Sorting criteria */
1691 ailSortCritType_t sortCrit = AILSC_DIST;
1692 if ((lua_gettop(L) > 1))
1693 sortCrit = AIL_toDistInt(L, 2);
1694
1695 int n = 0;
1697 for (Edict* checkPoint = level.ai_waypointList; checkPoint != nullptr; checkPoint = checkPoint->groupChain) {
1698 if (checkPoint->inuse)
1699 continue;
1700 if (checkPoint->getTeam() != AIL_ent->getTeam())
1701 continue;
1702 /* Don't walk to enemy ambush */
1703 Actor* check = nullptr;
1704 bool ambush = false;
1705 while ((check = G_EdictsGetNextLivingActorOfTeam(check, TEAM_ALIEN))) {
1706 const float dist = VectorDist(AIL_ent->origin, check->origin);
1707 /* @todo add visibility check here? */
1708 if (dist < minEnemyDist) {
1709 ambush = true;
1710 break;
1711 }
1712 }
1713 if (ambush)
1714 continue;
1715 switch (sortCrit) {
1716 case AILSC_PATH:
1717 {
1719 if (G_FindPath(0, AIL_ent, AIL_ent->pos, checkPoint->pos, false, ROUTING_NOT_REACHABLE - 1))
1720 move = gi.MoveLength(level.pathingMap, checkPoint->pos, 0, false);
1721 if (move < minDist * TU_MOVE_STRAIGHT)
1722 continue;
1723 if (checkPoint->count < AIL_ent->count) {
1724 sortTable[n].sortLookup = move;
1725 sortTable[++n].data = checkPoint;
1726 }
1727 }
1728 break;
1729 case AILSC_DIST:
1730 default:
1731 {
1732 const float dist = VectorDist(AIL_ent->origin, checkPoint->origin);
1733 if (dist < minDist * UNIT_SIZE)
1734 continue;
1735 if (checkPoint->count < AIL_ent->count) {
1736 sortTable[n].sortLookup = dist;
1737 sortTable[++n].data = checkPoint;
1738 }
1739 }
1740 break;
1741 }
1742 }
1743
1744 /* Sort by distance */
1745 std::sort(sortTable, sortTable + n);
1746
1747 /* Now save it in a Lua table. */
1748 lua_newtable(L);
1749 for (int i = 0; i < n; i++) {
1750 lua_pushnumber(L, i + 1); /* index, starts with 1 */
1751 lua_pushpos3(L, &sortTable[i].data->pos); /* value */
1752 lua_rawset(L, -3); /* store the value in the table */
1753 }
1754 return 1; /* Returns the table of positions. */
1755}
1756
1761static int AIL_positionmission (lua_State* L)
1762{
1763 /* check parameter */
1764 if (!(lua_gettop(L) && lua_ispos3(L, 1))) {
1766 lua_pushboolean(L, 0);
1767 return 1;
1768 }
1769 const pos3_t* target = lua_topos3(L, 1);
1770 int tus = AIL_ent->getUsableTUs();
1771 if (lua_gettop(L) > 1) {
1772 if (lua_isnumber(L, 2))
1773 tus = lua_tonumber(L, 2);
1774 else
1776 }
1777 G_MoveCalc(0, AIL_ent, AIL_ent->pos, tus);
1778 gi.MoveStore(level.pathingMap);
1779
1780 pos3_t oldPos;
1782 int radius = 3;
1783 const Edict* const mission = G_GetEdictFromPos(*target, ET_MISSION);
1784 if (mission)
1785 radius = mission->radius;
1786 if (AI_FindMissionLocation(AIL_ent, *target, tus, radius))
1787 lua_pushpos3(L, &AIL_ent->pos);
1788 else
1789 lua_pushboolean(L, 0);
1790
1792 return 1;
1793}
1794
1799static int AIL_positionwander (lua_State* L)
1800{
1801 /* Calculate move table. */
1803 gi.MoveStore(level.pathingMap);
1804
1805 /* Set defaults */
1806 int radius = (AIL_ent->getUsableTUs() + 1) / TU_MOVE_STRAIGHT;
1807 pos3_t center;
1808 VectorCopy(AIL_ent->pos, center);
1809 int method = AILPW_RAND;
1810 int tus = AIL_ent->getUsableTUs();
1811
1812 /* Check parameters */
1813 if (lua_gettop(L) > 0)
1814 method = AIL_toWanderPInt(L, 1);
1815 if (lua_gettop(L) > 1) {
1816 if (lua_isnumber(L, 2))
1817 radius = lua_tonumber(L, 2);
1818 else
1820 }
1821 if (lua_gettop(L) > 2) {
1822 if (lua_ispos3(L, 3))
1823 VectorCopy(*lua_topos3(L, 3), center);
1824 else
1826 }
1827
1828 if (lua_gettop(L) > 3) {
1829 if (lua_isnumber(L, 4))
1830 tus = std::min(static_cast<int>(lua_tonumber(L, 4)), tus);
1831 else
1833 }
1834
1835 vec3_t d;
1836 if (method > 0)
1837 VectorSubtract(AIL_ent->pos, center, d);
1838 const int cDir = method > 0 ? (VectorEmpty(d) ? AIL_ent->dir : AngleToDir(static_cast<int>(atan2(d[1], d[0]) * todeg))) : NONE;
1839 float bestScore = 0;
1840 pos3_t bestPos = {0, 0, PATHFINDING_HEIGHT};
1841 pos3_t pos;
1842 AiAreaSearch searchArea(center, radius);
1843 while (searchArea.getNext(pos)) {
1844 const pos_t move = G_ActorMoveLength(AIL_ent, level.pathingMap, pos, true);
1845 if (move >= ROUTING_NOT_REACHABLE || move > tus)
1846 continue;
1847 if (!AI_CheckPosition(AIL_ent, pos))
1848 continue;
1849 float score = 0.0f;
1850 switch (method) {
1851 case AILPW_RAND:
1852 score = rand();
1853 break;
1854 case AILPW_CW:
1855 case AILPW_CCW: {
1856 score = VectorDistSqr(center, pos);
1857 VectorSubtract(pos, center, d);
1858 int dir = AngleToDir(static_cast<int>(atan2(d[1], d[0]) * todeg));
1859 if (!(method == AILPW_CW && dir == dvright[cDir]) && !(method == AILPW_CCW && dir == dvleft[cDir]))
1860 for (int n = 1; n < 8; ++n) {
1861 dir = method == 1 ? dvleft[dir] : dvright[dir];
1862 score /= pow(n * 2.0f, 2);
1863 if ((method == 1 && dir == dvright[cDir]) || (method == 2 && dir == dvleft[cDir]))
1864 break;
1865 }
1866 }
1867 break;
1868 }
1869 if (score > bestScore) {
1870 bestScore = score;
1871 VectorCopy(pos, bestPos);
1872 }
1873 }
1874
1875 if (bestPos[2] >= PATHFINDING_HEIGHT) {
1876 lua_pushboolean(L, 0);
1877 return 1;
1878 }
1879 lua_pushpos3(L, &bestPos);
1880 return 1;
1881}
1882
1886static int AIL_findweapons (lua_State* L)
1887{
1888 bool full = false;
1889 if (lua_gettop(L) > 0) {
1890 if (lua_isboolean(L, 1))
1891 full = lua_toboolean(L, 1);
1892 else
1894 }
1895
1897 int n = 0;
1898 Edict* check = nullptr;
1899 while ((check = G_EdictsGetNextInUse(check))) {
1900 if (check->type != ET_ITEM)
1901 continue;
1902 if(!AI_CheckPosition(AIL_ent, check->pos))
1903 continue;
1905 continue;
1906 const pos_t move = G_ActorMoveLength(AIL_ent, level.pathingMap, check->pos, false);
1907 if (full || move <= AIL_ent->getUsableTUs() - INVDEF(CID_FLOOR)->out - INVDEF(CID_RIGHT)->in) {
1908 for (const Item* item = check->getFloor(); item; item = item->getNext()) {
1910 if (item->isWeapon() && (item->getAmmoLeft() > 0 || item->def()->ammo <= 0)) {
1911 sortTable[n].data = check;
1912 sortTable[n++].sortLookup = move;
1913 break;
1914 }
1915 }
1916 }
1917 }
1918
1919 /* Sort by distance */
1920 std::sort(sortTable, sortTable + n);
1921
1922 /* Now save it in a Lua table. */
1923 lua_newtable(L);
1924 for (int i = 0; i < n; i++) {
1925 lua_pushnumber(L, i + 1); /* index, starts with 1 */
1926 lua_pushpos3(L, &sortTable[i].data->pos); /* value */
1927 lua_rawset(L, -3); /* store the value in the table */
1928 }
1929 return 1; /* Returns the table of positions. */
1930}
1931
1935static int AIL_isfighter (lua_State* L)
1936{
1937 const bool result = AIL_ent->chr.teamDef->weapons || AIL_ent->chr.teamDef->onlyWeapon;
1938 lua_pushboolean(L, result);
1939 return 1;
1940}
1941
1945static int AIL_setwaypoint (lua_State* L)
1946{
1947 /* No waypoint, reset the count value to restart the search */
1948 if (lua_gettop(L) < 1 || lua_isnil(L, 1)) {
1949 AIL_ent->count = 100;
1950 lua_pushboolean(L, 1);
1951 } else if (lua_ispos3(L, 1)){
1952 pos3_t pos;
1954 Edict* waypoint = G_GetEdictFromPos(pos, ET_CIVILIANTARGET);
1955 if (waypoint != nullptr) {
1956 AIL_ent->count = waypoint->count;
1957 lua_pushboolean(L, 1);
1958 } else
1959 lua_pushboolean(L, 0);
1960 } else
1961 lua_pushboolean(L, 0);
1962
1963 return 1;
1964}
1965
1969static int AIL_difficulty (lua_State* L)
1970{
1971 lua_pushnumber(L, g_difficulty->value);
1972 return 1;
1973}
1974
1978static int AIL_positionflee (lua_State* L)
1979{
1980 int tus = AIL_ent->getUsableTUs();
1981 if (lua_gettop(L)) {
1982 if (lua_isnumber(L, 1))
1983 tus = std::min(static_cast<int>(lua_tonumber(L, 1)), tus);
1984 else
1986 }
1987
1988 /* Calculate move table. */
1990 pos3_t oldPos;
1992
1993 const int radius = (tus + 1) / TU_MOVE_STRAIGHT;
1994 float bestScore = -1;
1995 pos3_t bestPos = {0, 0, PATHFINDING_HEIGHT};
1996 AiAreaSearch searchArea(AIL_ent->pos, radius);
1997 while (searchArea.getNext(AIL_ent->pos)) {
1998 const pos_t move = G_ActorMoveLength(AIL_ent, level.pathingMap, AIL_ent->pos, false);
1999 if (move >= ROUTING_NOT_REACHABLE || move > tus)
2000 continue;
2002 continue;
2003 float minDistFoe = -1.0f, minDistFriend = -1.0f;
2004 Actor* check = nullptr;
2005 while ((check = G_EdictsGetNextLivingActor(check))) {
2006 const float dist = VectorDist(AIL_ent->origin, check->origin);
2007 if (check->isSameTeamAs(AIL_ent)) {
2008 if (dist < minDistFriend || minDistFriend < 0.0f)
2009 minDistFriend = dist;
2010 } else if (AI_IsHostile(AIL_ent, check) || AIL_ent->isPanicked()) {
2011 if (dist < minDistFoe || minDistFoe < 0.0f)
2012 minDistFoe = dist;
2013 }
2014 }
2015 float score = minDistFoe - (minDistFriend / GRID_WIDTH);
2016 /* Try to hide */
2019 score /= UNIT_SIZE;
2020 if (score > bestScore) {
2021 bestScore = score;
2022 VectorCopy(AIL_ent->pos, bestPos);
2023 }
2024 }
2026
2027 if (bestPos[2] == PATHFINDING_HEIGHT) {
2028 lua_pushboolean(L, 0);
2029 } else {
2030 lua_pushpos3(L, &bestPos);
2031 }
2032
2033 return 1;
2034}
2035
2039static int AIL_weapontype (lua_State* L)
2040{
2041 const Item* right = AIL_ent->getRightHandItem();
2042 const Item* left = AIL_ent->getLeftHandItem();
2043
2044 lua_pushstring(L, right ? right->def()->type : "none");
2045 lua_pushstring(L, left ? left->def()->type : "none");
2046
2047 return 2;
2048}
2049
2053static int AIL_actor (lua_State* L)
2054{
2055 aiActor_t actor = {AIL_ent};
2056 lua_pushactor(L, &actor);
2057 return 1;
2058}
2062static int AIL_tusforshooting (lua_State* L)
2063{
2064 int bestTUs = 256;
2065 const Item* weapon = AIL_ent->getRightHandItem();
2066 if (weapon) {
2067 const fireDef_t* fd = weapon->getFastestFireDef();
2068 if (fd)
2069 bestTUs = fd->time;
2070 }
2071 weapon = AIL_ent->getLeftHandItem();
2072 if (weapon) {
2073 const fireDef_t* fd = weapon->getFastestFireDef();
2074 if (fd) {
2075 const int tus = weapon->getFastestFireDef()->time;
2076 if (tus < bestTUs)
2077 bestTUs = tus;
2078 }
2079 }
2080
2081 lua_pushnumber(L, bestTUs);
2082 return 1;
2083}
2084
2088static int AIL_class (lua_State* L)
2089{
2090 lua_pushstring(L, AIL_ent->AI.subtype);
2091 return 1;
2092}
2093
2097static int AIL_hideneeded (lua_State* L)
2098{
2099 lua_pushboolean(L, AI_HideNeeded(AIL_ent));
2100 return 1;
2101}
2102
2108void AIL_ActorThink (Player& player, Actor* actor)
2109{
2110 /* Set the global player and edict */
2111 AIL_ent = actor;
2112 AIL_player = &player;
2113
2114 /* Try to run the function. */
2115 lua_getglobal(ailState, actor->AI.type);
2116 if (lua_istable(ailState, -1)) {
2117 lua_getfield(ailState, -1, "think");
2118 if (lua_pcall(ailState, 0, 0, 0)) { /* error has occured */
2119 gi.DPrintf("Error while running Lua: %s\n",
2120 lua_isstring(ailState, -1) ? lua_tostring(ailState, -1) : "Unknown Error");
2121 }
2122 } else {
2123 gi.DPrintf("Error while running Lua: AI for %s not found!\n", actor->AI.type);
2124 }
2125
2126 /* Cleanup */
2127 AIL_ent = nullptr;
2128 AIL_player = nullptr;
2129}
2130
2134static const char* AIL_GetAIType (const int team)
2135{
2136 const char* type;
2137 switch (team) {
2138 case TEAM_ALIEN:
2139 type = "alien";
2140 break;
2141 case TEAM_CIVILIAN:
2142 type = "civilian";
2143 break;
2144 case TEAM_PHALANX:
2145 default: /* Default to "soldier" AI for multiplayer teams */
2146 type = "soldier";
2147 break;
2148 }
2149 return type;
2150}
2151
2156bool AIL_TeamThink (Player& player)
2157{
2158 /* Set the global player */
2159 AIL_player = &player;
2160 AIL_ent = nullptr;
2161
2162 bool thinkAgain = false;
2163 /* Try to run the function. */
2164 lua_getglobal(ailState, AIL_GetAIType(player.getTeam()));
2165 if (lua_istable(ailState, -1)) {
2166 lua_getfield(ailState, -1, "team_think");
2167 if (lua_pcall(ailState, 0, 1, 0)) { /* error has occured */
2168 gi.DPrintf("Error while running Lua: %s\n",
2169 lua_isstring(ailState, -1) ? lua_tostring(ailState, -1) : "Unknown Error");
2170 } else
2171 thinkAgain = lua_toboolean(ailState, -1);
2172 } else {
2173 gi.DPrintf("Error while running Lua: AI for %s not found!\n", AIL_toTeamString(player.getTeam()));
2174 }
2175
2176 /* Cleanup */
2177 AIL_player = nullptr;
2178 AIL_ent = nullptr;
2179 return thinkAgain;
2180}
2181
2185static lua_State* AIL_InitLua () {
2186 /* Create the Lua state */
2187 lua_State* newState = luaL_newstate();
2188
2189 /* Register metatables. */
2190 actorL_register(newState);
2191 pos3L_register(newState);
2192
2193 /* Register libraries. */
2194 luaL_register(newState, AI_METATABLE, AIL_methods);
2195
2196 return newState;
2197}
2204{
2205 /* Prepare the AI */
2206 AI_t* AI = &actor->AI;
2207 Q_strncpyz(AI->type, AIL_GetAIType(actor->getTeam()), sizeof(AI->type));
2208 Q_strncpyz(AI->subtype, actor->chr.teamDef->id, sizeof(AI->subtype));
2209
2210 /* Create the a new Lua state if needed */
2211 if (ailState == nullptr)
2213
2214 if (ailState == nullptr) {
2215 gi.DPrintf("Unable to create Lua state.\n");
2216 return -1;
2217 }
2218
2219 /* Load the AI if needed */
2220 lua_getglobal(ailState, AI->type);
2221 if (!lua_istable(ailState, -1)) {
2222 char path[MAX_VAR];
2223 Com_sprintf(path, sizeof(path), "ai/%s.lua", AI->type);
2224 char* fbuf;
2225 const int size = gi.FS_LoadFile(path, (byte**) &fbuf);
2226 if (size == 0) {
2227 gi.DPrintf("Unable to load Lua file '%s'.\n", path);
2228 return -1;
2229 }
2230 if (luaL_dobuffer(ailState, fbuf, size, path)) {
2231 gi.DPrintf("Unable to parse Lua file '%s'\n", path);
2232 gi.DPrintf("%s\n", lua_isstring(ailState, -1) ? lua_tostring(ailState, -1) : "Unknown Error");
2233 gi.FS_FreeFile(fbuf);
2234 return -1;
2235 }
2236 lua_setglobal(ailState, AI->type);
2237 gi.FS_FreeFile(fbuf);
2238 } else {
2239 lua_pop(ailState, 1);
2240 }
2241
2242 return 0;
2243}
2244
2245void AIL_Init (void)
2246{
2247 gi.RegisterConstInt("luaaiteam::phalanx", TEAM_PHALANX);
2248 gi.RegisterConstInt("luaaiteam::civilian", TEAM_CIVILIAN);
2249 gi.RegisterConstInt("luaaiteam::alien", TEAM_ALIEN);
2250 gi.RegisterConstInt("luaaiteam::all", TEAM_ALL);
2251
2252 gi.RegisterConstInt("luaaivis::all", AILVT_ALL);
2253 gi.RegisterConstInt("luaaivis::sight", AILVT_SIGHT);
2254 gi.RegisterConstInt("luaaivis::team", AILVT_TEAM);
2255 gi.RegisterConstInt("luaaivis::extra", AILVT_DIST);
2256
2257 gi.RegisterConstInt("luaaisort::dist", AILSC_DIST);
2258 gi.RegisterConstInt("luaaisort::path", AILSC_PATH);
2259 gi.RegisterConstInt("luaaisort::HP", AILSC_HP);
2260
2261 gi.RegisterConstInt("luaaidist::dist", AILSC_DIST);
2262 gi.RegisterConstInt("luaaidist::path", AILSC_PATH);
2263
2264 gi.RegisterConstInt("luaaishot::fastest", AILSP_FAST);
2265 gi.RegisterConstInt("luaaishot::nearest", AILSP_NEAR);
2266 gi.RegisterConstInt("luaaishot::farthest", AILSP_FAR);
2267 gi.RegisterConstInt("luaaishot::best_dam", AILSP_DMG);
2268
2269 gi.RegisterConstInt("luaaiwander::rand", AILPW_RAND);
2270 gi.RegisterConstInt("luaaiwander::CW", AILPW_CW);
2271 gi.RegisterConstInt("luaaiwander::CCW", AILPW_CCW);
2272
2273 ailState = nullptr;
2274}
2275
2276void AIL_Shutdown (void)
2277{
2278 gi.UnregisterConstVariable("luaaiteam::phalanx");
2279 gi.UnregisterConstVariable("luaaiteam::civilian");
2280 gi.UnregisterConstVariable("luaaiteam::alien");
2281 gi.UnregisterConstVariable("luaaiteam::all");
2282
2283 gi.UnregisterConstVariable("luaaivis::all");
2284 gi.UnregisterConstVariable("luaaivis::sight");
2285 gi.UnregisterConstVariable("luaaivis::team");
2286 gi.UnregisterConstVariable("luaaivis::extra");
2287
2288 gi.UnregisterConstVariable("luaaisort::dist");
2289 gi.UnregisterConstVariable("luaaisort::path");
2290 gi.UnregisterConstVariable("luaaisort::HP");
2291
2292 gi.UnregisterConstVariable("luaaidist::dist");
2293 gi.UnregisterConstVariable("luaaidist::path");
2294
2295 gi.UnregisterConstVariable("luaaishot::fastest");
2296 gi.UnregisterConstVariable("luaaishot::nearest");
2297 gi.UnregisterConstVariable("luaaishot::farthest");
2298 gi.UnregisterConstVariable("luaaishot::best_dam");
2299
2300 gi.UnregisterConstVariable("luaaiwander::rand");
2301 gi.UnregisterConstVariable("luaaiwander::CW");
2302 gi.UnregisterConstVariable("luaaiwander::CCW");
2303}
2304
2308void AIL_Cleanup (void)
2309{
2310 lua_close(ailState);
2311 ailState = nullptr;
2312}
#define INVDEF(containerID)
Definition: cl_shared.h:48
An Edict of type Actor.
Definition: g_edict.h:348
bool isDead() const
Definition: g_edict.h:362
bool isPanicked() const
Definition: g_edict.h:356
int getMorale() const
Definition: g_edict.h:350
bool isInsane() const
Definition: g_edict.h:359
int getUsableTUs() const
Calculates the amount of usable TUs. This is without the reserved TUs.
Definition: g_edict.h:400
bool isRaged() const
Definition: g_edict.h:358
bool isReaction() const
Definition: g_edict.h:357
bool isCrouched() const
Definition: g_edict.h:361
AiAreaSearch class, used to get an area of the map around a certain position for the AI to check poss...
Definition: g_ai.h:33
bool getNext(pos3_t pos)
Get next position in the search area.
Definition: g_ai.cpp:79
Definition: g_edict.h:45
int getPlayerNum() const
Definition: g_edict.h:234
bool isSameTeamAs(const Edict *other) const
Definition: g_edict.h:278
character_t chr
Definition: g_edict.h:116
Item * getRightHandItem() const
Definition: g_edict.h:249
vec3_t origin
Definition: g_edict.h:53
pos3_t pos
Definition: g_edict.h:55
int HP
Definition: g_edict.h:89
byte dir
Definition: g_edict.h:86
AI_t AI
Definition: g_edict.h:171
Edict * groupChain
Definition: g_edict.h:167
void setOrigin(const pos3_t newPos)
Set the edict's pos and origin vector to the given grid position.
Definition: g_edict.h:223
Item * getLeftHandItem() const
Definition: g_edict.h:252
int getTeam() const
Definition: g_edict.h:269
void calcOrigin()
Calculate the edict's origin vector from it's grid position.
Definition: g_edict.h:216
int count
Definition: g_edict.h:135
bool isSamePosAs(const pos3_t cmpPos)
Check whether the edict is on the given position.
Definition: g_edict.h:286
int radius
Definition: g_edict.h:123
entity_type_t type
Definition: g_edict.h:81
Item * getFloor() const
Definition: g_edict.h:262
item instance data, with linked list capability
Definition: inv_shared.h:402
const objDef_t * ammoDef(void) const
Definition: inv_shared.h:460
int getAmmoLeft() const
Definition: inv_shared.h:466
const fireDef_t * getFastestFireDef() const
Definition: inv_shared.cpp:624
const objDef_t * def(void) const
Definition: inv_shared.h:469
const fireDef_t * getFiredefs() const
Returns the firedefinitions for a given weapon/ammo.
Definition: inv_shared.cpp:576
bool isHeldTwoHanded() const
Definition: inv_shared.h:476
Item * getNext() const
Definition: inv_shared.h:451
#define ROUTING_NOT_REACHABLE
Definition: defines.h:283
#define MAX_EDICTS
Definition: defines.h:99
#define TU_MOVE_STRAIGHT
Definition: defines.h:74
#define NONE
Definition: defines.h:68
#define TEAM_DEFAULT
Definition: defines.h:51
#define GRID_WIDTH
absolute max - -GRID_WIDTH up tp +GRID_WIDTH
Definition: defines.h:290
#define UNIT_SIZE
Definition: defines.h:121
#define ROUTING_UNREACHABLE
Definition: defines.h:284
#define PATHFINDING_HEIGHT
15 max, adjusting above 8 will require a rewrite to the DV code
Definition: defines.h:294
int G_ActorGetModifiedTimeForFiredef(const Edict *const ent, const fireDef_t *const fd, const bool reaction)
Definition: g_actor.cpp:764
bool G_ActorInvMove(Actor *actor, const invDef_t *fromContType, Item *fItem, const invDef_t *toContType, int tx, int ty, bool checkaction)
Moves an item inside an inventory. Floors are handled special.
Definition: g_actor.cpp:506
#define G_IsDead(ent)
Definition: g_actor.h:34
bool AI_FindMissionLocation(Actor *actor, const pos3_t to, int tus, int radius)
Try to go close to a mission edict.
Definition: g_ai.cpp:1423
bool AI_CheckLineOfFire(const Actor *shooter, const Edict *target, const fireDef_t *fd, int shots)
Definition: g_ai.cpp:763
bool AI_TryToReloadWeapon(Actor *actor, containerIndex_t containerID)
if a weapon can be reloaded we attempt to do so if TUs permit, otherwise drop it
Definition: g_ai.cpp:1574
void AI_TurnIntoDirection(Actor *actor, const pos3_t pos)
This function will turn the AI actor into the direction that is needed to walk to the given location.
Definition: g_ai.cpp:1557
bool AI_HideNeeded(const Actor *actor)
Checks whether the given alien should try to hide because there are enemies close enough to shoot the...
Definition: g_ai.cpp:484
bool AI_FindHerdLocation(Actor *actor, const pos3_t from, const vec3_t target, int tu, bool inverse)
Tries to search a spot where actor will be more closer to the target and behind the target from enemy...
Definition: g_ai.cpp:659
bool AI_CheckPosition(const Actor *const actor, const pos3_t pos)
Checks if the given position is safe to stand on.
Definition: g_ai.cpp:581
const Item * AI_GetItemForShootType(shoot_types_t shootType, const Edict *ent)
Definition: g_ai.cpp:541
int AI_GetHidingTeam(const Edict *ent)
Returns the value for the vis check whenever an ai actor tries to hide. For aliens this is the invers...
Definition: g_ai.cpp:570
float AI_CalcShotDamage(Actor *actor, const Actor *target, const fireDef_t *fd, shoot_types_t shotType)
Calculate estimated damage per single shoot.
Definition: g_ai.cpp:811
bool AI_FighterCheckShoot(const Actor *actor, const Edict *check, const fireDef_t *fd, float dist)
Check whether the fighter should perform the shoot.
Definition: g_ai.cpp:364
bool AI_FindHidingLocation(int team, Actor *actor, const pos3_t from, int tuLeft)
Tries to search a hiding spot.
Definition: g_ai.cpp:606
bool AI_IsHostile(const Actor *actor, const Edict *target)
Check if actor perceives target as hostile.
Definition: g_ai.cpp:933
const invDef_t * AI_SearchGrenade(const Actor *actor, Item **ip)
Search the edict's inventory for a grenade or other one-use weapon.
Definition: g_ai.cpp:954
Artificial Intelligence functions.
static int AIL_class(lua_State *L)
Returns the AI actor's class.
Definition: g_ai_lua.cpp:2088
static ailWanderPosType AIL_toWanderPInt(lua_State *L, const int index)
Return wander position type int representation from the string representation in the lua stack.
Definition: g_ai_lua.cpp:203
static int AIL_toTeamInt(const char *team, const int param)
Converts team string into int representation.
Definition: g_ai_lua.cpp:117
static ailSortCritType_t AIL_toDistInt(lua_State *L, const int index)
Return distance type int representation from the string representation in the lua stack.
Definition: g_ai_lua.cpp:167
bool operator<(AilSortTable< T > i, AilSortTable< T > j)
Definition: g_ai_lua.cpp:231
static int AIL_positionhide(lua_State *L)
Moves the actor into a position in which he can hide.
Definition: g_ai_lua.cpp:1433
static int AIL_setwaypoint(lua_State *L)
Mark the current waypoint for a civ.
Definition: g_ai_lua.cpp:1945
static int actorL_isinjured(lua_State *L)
Checks to see if the actor is seriously injured.
Definition: g_ai_lua.cpp:761
static int AIL_reload(lua_State *L)
Actor reloads his weapons.
Definition: g_ai_lua.cpp:1267
static lua_State * AIL_InitLua()
Definition: g_ai_lua.cpp:2185
ailSortCritType_t
target sorting criteria (lowest first)
Definition: g_ai_lua.cpp:73
@ AILSC_DIST
Definition: g_ai_lua.cpp:74
@ AILSC_HP
Definition: g_ai_lua.cpp:76
@ AILSC_PATH
Definition: g_ai_lua.cpp:75
static ailSortCritType_t AIL_toSortInt(lua_State *L, const int index)
Return sort type int representation from the string representation in the lua stack.
Definition: g_ai_lua.cpp:149
static int AIL_reactionfire(lua_State *L)
Sets the actor's reaction fire mode.
Definition: g_ai_lua.cpp:1210
static int actorL_pos(lua_State *L)
Gets the actors position.
Definition: g_ai_lua.cpp:511
static int AIL_roundsleft(lua_State *L)
Checks to see how many rounds the actor has left.
Definition: g_ai_lua.cpp:1235
#define luaL_dobuffer(L, b, n, s)
Definition: g_ai_lua.cpp:59
static int AIL_positionherd(lua_State *L)
Determine the position where actor is closest to the target and with the target located between the a...
Definition: g_ai_lua.cpp:1489
#define AIL_invalidparameter(n)
Definition: g_ai_lua.cpp:61
static int actorL_morale(lua_State *L)
Gets the current morale of the actor onto the stack.
Definition: g_ai_lua.cpp:733
static int AIL_missiontargets(lua_State *L)
Returns the positions of the available mission targets.
Definition: g_ai_lua.cpp:1595
static int AIL_grabweapon(lua_State *L)
Actor tries to grab a weapon from inventory.
Definition: g_ai_lua.cpp:1296
static int AIL_actor(lua_State *L)
Returns the currently moving AI actor.
Definition: g_ai_lua.cpp:2053
static int actorL_HP(lua_State *L)
Gets the number of HP the actor has left.
Definition: g_ai_lua.cpp:715
static const luaL_reg pos3L_methods[]
Definition: g_ai_lua.cpp:344
static int lua_ispos3(lua_State *L, int index)
Checks to see if there is a pos3 metatable at index in the lua_State.
Definition: g_ai_lua.cpp:861
ailVisType_t
vis check types
Definition: g_ai_lua.cpp:65
@ AILVT_SIGHT
Definition: g_ai_lua.cpp:67
@ AILVT_DIST
Definition: g_ai_lua.cpp:69
@ AILVT_ALL
Definition: g_ai_lua.cpp:66
@ AILVT_TEAM
Definition: g_ai_lua.cpp:68
static ailVisType_t AIL_toVisInt(lua_State *L, const int index)
Return visibility mode int representation from the string representation in the lua stack.
Definition: g_ai_lua.cpp:131
int AIL_InitActor(Actor *actor)
Initializes the lua AI for an actor.
Definition: g_ai_lua.cpp:2203
static Actor * AIL_ent
Definition: g_ai_lua.cpp:238
static int actorL_isdead(lua_State *L)
Check if the actor is dead.
Definition: g_ai_lua.cpp:799
static int AIL_positionapproach(lua_State *L)
Approach to a target actor.
Definition: g_ai_lua.cpp:1531
ailWanderPosType
Definition: g_ai_lua.cpp:87
@ AILPW_CCW
Definition: g_ai_lua.cpp:90
@ AILPW_RAND
Definition: g_ai_lua.cpp:88
@ AILPW_CW
Definition: g_ai_lua.cpp:89
#define ACTOR_METATABLE
Definition: g_ai_lua.cpp:52
static int actorL_isvalidtarget(lua_State *L)
Check if the actor is a valid target to attack.
Definition: g_ai_lua.cpp:817
#define POS3_METATABLE
Definition: g_ai_lua.cpp:51
static int pos3L_goto(lua_State *L)
Makes the actor head to the position.
Definition: g_ai_lua.cpp:918
static int AIL_hideneeded(lua_State *L)
Check if the actor needs wants to hide.
Definition: g_ai_lua.cpp:2097
static int AIL_difficulty(lua_State *L)
Return the difficulty number (in case we want different AI for different ones)
Definition: g_ai_lua.cpp:1969
static const char * AIL_toTeamString(const int team)
Converts integer team representation into string.
Definition: g_ai_lua.cpp:102
void AIL_Init(void)
Definition: g_ai_lua.cpp:2245
static int AIL_findweapons(lua_State *L)
Returns a table of the positions of nearby usable weapons on the floor.
Definition: g_ai_lua.cpp:1886
static int lua_isactor(lua_State *L, int index)
Checks to see if there is a actor metatable at index in the lua_State.
Definition: g_ai_lua.cpp:454
static int AIL_positionshoot(lua_State *L)
Moves the actor into a position in which he can shoot his target.
Definition: g_ai_lua.cpp:1305
static pos3_t * lua_pushpos3(lua_State *L, pos3_t *pos)
Pushes a pos3 as a metatable at the top of the stack.
Definition: g_ai_lua.cpp:890
static int actorL_shoot(lua_State *L)
Shoots the actor.
Definition: g_ai_lua.cpp:523
static const luaL_reg AIL_methods[]
Definition: g_ai_lua.cpp:388
static int actorL_register(lua_State *L)
Registers the actor metatable in the lua_State.
Definition: g_ai_lua.cpp:430
static int AIL_weapontype(lua_State *L)
Returns the type of the weapons in the actors hands.
Definition: g_ai_lua.cpp:2039
void AIL_Cleanup(void)
Closes the LUA AI.
Definition: g_ai_lua.cpp:2308
static int pos3L_register(lua_State *L)
Registers the pos3 metatable in the lua_State.
Definition: g_ai_lua.cpp:837
static ailShootPosType_t AIL_toShotPInt(lua_State *L, const int index)
Return shooting position type int representation from the string representation in the lua stack.
Definition: g_ai_lua.cpp:185
static int AIL_select(lua_State *L)
Select an specific AI actor.
Definition: g_ai_lua.cpp:1075
void AIL_Shutdown(void)
Definition: g_ai_lua.cpp:2276
static int AIL_tusforshooting(lua_State *L)
Returns the min TUs the actor needs to fire.
Definition: g_ai_lua.cpp:2062
static int AIL_print(lua_State *L)
Works more or less like Lua's builtin print.
Definition: g_ai_lua.cpp:1000
static int AIL_positionflee(lua_State *L)
Return a position to flee to.
Definition: g_ai_lua.cpp:1978
static int actorL_team(lua_State *L)
Gets the actor's team.
Definition: g_ai_lua.cpp:566
static int AIL_crouch(lua_State *L)
Requests a crouch state (with true/false) and returns current crouch state.
Definition: g_ai_lua.cpp:1191
static const char * AIL_GetAIType(const int team)
Return the AI type for the given team (the lua file the team actors should run)
Definition: g_ai_lua.cpp:2134
static int AIL_see(lua_State *L)
Returns what the actor can see.
Definition: g_ai_lua.cpp:1099
bool AIL_TeamThink(Player &player)
The team think function for the ai controlled players.
Definition: g_ai_lua.cpp:2156
static int actorL_throwgrenade(lua_State *L)
Throws a grenade to the actor.
Definition: g_ai_lua.cpp:580
static int AIL_waypoints(lua_State *L)
Return the positions of the next waypoints.
Definition: g_ai_lua.cpp:1678
#define AI_METATABLE
Definition: g_ai_lua.cpp:53
static int pos3L_distance(lua_State *L)
Return the distance the position an the AI actor.
Definition: g_ai_lua.cpp:961
static int pos3L_face(lua_State *L)
Makes the actor face the position.
Definition: g_ai_lua.cpp:947
static const luaL_reg actorL_methods[]
Definition: g_ai_lua.cpp:312
static int AIL_squad(lua_State *L)
Returns a table with the actors in the current player's team.
Definition: g_ai_lua.cpp:1047
static int actorL_tostring(lua_State *L)
Pushes the actor as a string.
Definition: g_ai_lua.cpp:495
static pos3_t * lua_topos3(lua_State *L, int index)
Returns the pos3 from the metatable at index.
Definition: g_ai_lua.cpp:878
static int actorL_TU(lua_State *L)
Gets the number of usable TU the actor has left.
Definition: g_ai_lua.cpp:697
static int AIL_positionwander(lua_State *L)
Return a new position to move to.
Definition: g_ai_lua.cpp:1799
static Player * AIL_player
Definition: g_ai_lua.cpp:239
static int pos3L_tostring(lua_State *L)
Puts the pos3 information in a string.
Definition: g_ai_lua.cpp:902
static aiActor_t * lua_toactor(lua_State *L, int index)
Returns the actor from the metatable at index.
Definition: g_ai_lua.cpp:471
static float AIL_GetBestShot(const Actor &shooter, const Actor &target, const int tu, const float dist, shoot_types_t &bestType, fireDefIndex_t &bestFd, int &bestShots)
Definition: g_ai_lua.cpp:241
static int AIL_canreload(lua_State *L)
Checks to see if the actor can reload.
Definition: g_ai_lua.cpp:1257
ailShootPosType_t
Shooting position types.
Definition: g_ai_lua.cpp:80
@ AILSP_FAST
Definition: g_ai_lua.cpp:81
@ AILSP_DMG
Definition: g_ai_lua.cpp:84
@ AILSP_FAR
Definition: g_ai_lua.cpp:83
@ AILSP_NEAR
Definition: g_ai_lua.cpp:82
static int AIL_positionmission(lua_State *L)
Try to find a position nearby to the given position.
Definition: g_ai_lua.cpp:1761
void AIL_ActorThink(Player &player, Actor *actor)
The think function for the ai controlled players.
Definition: g_ai_lua.cpp:2108
static int actorL_isarmed(lua_State *L)
Check if actor has weapons.
Definition: g_ai_lua.cpp:780
static aiActor_t * lua_pushactor(lua_State *L, aiActor_t *actor)
Pushes a actor as a metatable at the top of the stack.
Definition: g_ai_lua.cpp:483
static lua_State * ailState
Definition: g_ai_lua.cpp:55
static int AIL_isfighter(lua_State *L)
Whether the current AI actor is a fighter or not.
Definition: g_ai_lua.cpp:1935
void G_ClientStateChange(const Player &player, Actor *actor, int reqState, bool checkaction)
Changes the state of a player/soldier.
Definition: g_client.cpp:473
bool G_ClientCanReload(Actor *actor, containerIndex_t containerID)
Returns true if actor can reload weapon.
Definition: g_client.cpp:536
bool G_ClientGetWeaponFromInventory(Actor *actor)
Retrieve or collect a loaded weapon from any linked container for the actor's right hand.
Definition: g_client.cpp:568
Interface for g_client.cpp.
bool G_ClientShoot(const Player &player, Actor *actor, const pos3_t at, shoot_types_t shootType, fireDefIndex_t firemode, shot_mock_t *mock, bool allowReaction, int z_align)
Setup for shooting, either real or mock.
Definition: g_combat.cpp:1183
All parts of the main game logic that are combat related.
Actor * G_EdictsGetNextLivingActorOfTeam(Actor *lastEnt, const int team)
Iterate through the living actor entities of the given team.
Definition: g_edicts.cpp:216
Actor * G_EdictsGetNextActor(Actor *lastEnt)
Iterate through the actor entities (even the dead!)
Definition: g_edicts.cpp:231
Actor * G_EdictsGetNextLivingActor(Actor *lastEnt)
Iterate through the living actor entities.
Definition: g_edicts.cpp:196
Edict * G_EdictsGetNextInUse(Edict *lastEnt)
Iterate through the entities that are in use.
Definition: g_edicts.cpp:166
functions to handle the storage and lifecycle of all edicts in the game module.
bool G_IsActorWounded(const Edict *ent, bool serious)
Definition: g_health.cpp:213
Local definitions for game module.
level_locals_t level
Definition: g_main.cpp:38
#define G_IsVisibleForTeam(ent, team)
Definition: g_local.h:144
game_import_t gi
Definition: g_main.cpp:39
cvar_t * mor_brave
Definition: g_main.cpp:104
cvar_t * g_ailua
Definition: g_main.cpp:112
cvar_t * g_difficulty
Definition: g_main.cpp:125
pos_t G_ActorMoveLength(const Actor *actor, const pathing_t *path, const pos3_t to, bool stored)
Return the needed TUs to walk to a given position.
Definition: g_move.cpp:270
void G_ClientMove(const Player &player, int visTeam, Actor *actor, const pos3_t to)
Generates the client events that are send over the netchannel to move an actor.
Definition: g_move.cpp:307
bool G_FindPath(int team, const Edict *movingActor, const pos3_t from, const pos3_t targetPos, bool crouched, int maxTUs)
Definition: g_move.cpp:107
void G_MoveCalc(int team, const Actor *movingActor, const pos3_t from, int distance)
Precalculates a move table for a given team and a given starting position. This will calculate a rout...
Definition: g_move.cpp:88
Edict * G_GetEdictFromPos(const pos3_t pos, const entity_type_t type)
Searches an edict of the given type at the given grid location.
Definition: g_utils.cpp:59
bool G_TestLineWithEnts(const vec3_t start, const vec3_t end)
fast version of a line trace including entities
Definition: g_utils.cpp:237
Misc utility functions for game module.
int G_TestVis(const int team, Edict *check, const vischeckflags_t flags)
test if check is visible by team (or if visibility changed?)
Definition: g_vis.cpp:255
int G_VisCheckDist(const Edict *const ent)
Definition: g_vis.cpp:163
int G_CheckVisTeamAll(const int team, const vischeckflags_t visFlags, const Edict *ent)
Do G_CheckVisTeam for all entities ent is the one that is looking at the others.
Definition: g_vis.cpp:376
bool G_Vis(const int team, const Edict *from, const Edict *check, const vischeckflags_t flags)
test if check is visible by from
Definition: g_vis.cpp:183
float G_ActorVis(const Edict *ent, const Edict *check, bool full)
calculate how much check is "visible" by ent
Definition: g_vis.cpp:100
#define ACTOR_VIS_10
Definition: g_vis.h:63
#define VT_NOFRUSTUM
Definition: g_vis.h:55
#define VT_PERISHCHK
Definition: g_vis.h:53
#define TEAM_ALL
Definition: g_vis.h:32
#define VS_YES
Definition: g_vis.h:46
int32_t fireDefIndex_t
Definition: inv_shared.h:78
#define CID_MAX
Definition: inv_shared.h:57
int32_t containerIndex_t
Definition: inv_shared.h:46
#define CID_FLOOR
Definition: inv_shared.h:55
#define CID_LEFT
Definition: inv_shared.h:48
#define CID_RIGHT
Definition: inv_shared.h:47
voidpf void uLong size
Definition: ioapi.h:42
voidpf void * buf
Definition: ioapi.h:42
const byte dvleft[CORE_DIRECTIONS]
Definition: mathlib.cpp:119
const byte dvright[CORE_DIRECTIONS]
Definition: mathlib.cpp:116
int AngleToDir(int angle)
Returns the index of array directionAngles[DIRECTIONS] whose value is the closest to angle.
Definition: mathlib.cpp:130
#define PosSubDV(p, crouch, dv)
Definition: mathlib.h:254
#define PosToVec(p, v)
Pos boundary size is +/- 128 - to get into the positive area we add the possible max negative value a...
Definition: mathlib.h:110
#define todeg
Definition: mathlib.h:51
#define TEAM_PHALANX
Definition: q_shared.h:62
#define TEAM_ALIEN
Definition: q_shared.h:63
@ ET_CIVILIANTARGET
Definition: q_shared.h:161
@ ET_MISSION
Definition: q_shared.h:162
@ ET_ITEM
Definition: q_shared.h:149
#define STATE_CROUCHED
Definition: q_shared.h:263
#define STATE_REACTION
Definition: q_shared.h:272
#define ST_NUM_SHOOT_TYPES
Amount of shoottypes available.
Definition: q_shared.h:236
int32_t shoot_types_t
Available shoot types - also see the ST_ constants.
Definition: q_shared.h:206
#define ST_RIGHT
The right hand should be used for shooting.
Definition: q_shared.h:211
#define TEAM_CIVILIAN
Definition: q_shared.h:61
#define ST_LEFT
The left hand should be used for shooting.
Definition: q_shared.h:221
QGL_EXTERN GLsizei const GLvoid * data
Definition: r_gl.h:89
QGL_EXTERN GLuint GLsizei GLsizei * length
Definition: r_gl.h:110
QGL_EXTERN GLuint index
Definition: r_gl.h:110
QGL_EXTERN GLint i
Definition: r_gl.h:113
QGL_EXTERN GLint GLenum type
Definition: r_gl.h:94
#define Q_streq(a, b)
Definition: shared.h:136
#define MAX_VAR
Definition: shared.h:36
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition: shared.cpp:457
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition: shared.cpp:494
Artificial intelligence of a character.
Definition: g_local.h:305
char type[MAX_QPATH]
Definition: g_local.h:306
char subtype[MAX_VAR]
Definition: g_local.h:307
float sortLookup
Definition: g_ai_lua.cpp:227
Wrapper around edict.
Definition: g_ai_lua.cpp:218
Actor * actor
Definition: g_ai_lua.cpp:219
const teamDef_t * teamDef
Definition: chr_shared.h:413
char name[MAX_VAR]
Definition: chr_shared.h:390
float value
Definition: cvar.h:80
int integer
Definition: cvar.h:81
this is a fire definition for our weapons/ammo
Definition: inv_shared.h:110
fireDefIndex_t fdIdx
Definition: inv_shared.h:130
float splrad
Definition: inv_shared.h:161
weaponFireDefIndex_t weapFdsIdx
Definition: inv_shared.h:126
bool rolled
Definition: inv_shared.h:138
bool launched
Definition: inv_shared.h:137
bool gravity
Definition: inv_shared.h:136
const char *IMPORT * GetConstVariable(const char *space, int value)
inventory definition for our menus
Definition: inv_shared.h:371
pathing_t * pathingMap
Definition: g_local.h:106
Edict * ai_waypointList
Definition: g_local.h:118
const char * type
Definition: inv_shared.h:271
int ammo
Definition: inv_shared.h:293
bool weapons
Definition: chr_shared.h:335
const objDef_t * onlyWeapon
Definition: chr_shared.h:336
char id[MAX_VAR]
Definition: chr_shared.h:309
pos_t pos3_t[3]
Definition: ufotypes.h:58
byte pos_t
Definition: ufotypes.h:57
vec_t vec3_t[3]
Definition: ufotypes.h:39
static int oldPos
#define VectorDist(a, b)
Definition: vector.h:69
#define VectorSubtract(a, b, dest)
Definition: vector.h:45
#define VectorCopy(src, dest)
Definition: vector.h:51
#define VectorEmpty(a)
Definition: vector.h:73
#define VectorDistSqr(a, b)
Definition: vector.h:68
#define VectorSet(v, x, y, z)
Definition: vector.h:59