UFO: Alien Invasion
Loading...
Searching...
No Matches
g_ai_lua.cpp
Go to the documentation of this file.
1
14
15/*
16Copyright (C) 2002-2025 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#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM < 504
52LUALIB_API int luaL_typeerror (lua_State *L, int narg, const char *tname) {
53 const char *msg = lua_pushfstring(L, "%s expected, got %s",
54 tname, luaL_typename(L, narg));
55 return luaL_argerror(L, narg, msg);
56}
57#endif
58
59#define POS3_METATABLE "pos3"
60#define ACTOR_METATABLE "actor"
61#define AI_METATABLE "ai"
62
63static lua_State* ailState;
67#define luaL_dobuffer(L, b, n, s) \
68 (luaL_loadbuffer(L, b, n, s) || lua_pcall(L, 0, LUA_MULTRET, 0))
69#define AIL_invalidparameter(n) \
70 gi.DPrintf("AIL: Invalid parameter #%d in '%s'.\n", n, __func__)
71
73typedef enum {
74 AILVT_ALL, /* Don't do vis checks (god's view) */
75 AILVT_SIGHT, /* Standard vis check */
76 AILVT_TEAM, /* Team vis check */
77 AILVT_DIST /* Check only vis distance */
79
81typedef enum {
82 AILSC_DIST, /* Sort by line distance */
83 AILSC_PATH, /* Sort by pathing cost */
84 AILSC_HP /* Sort by HP */
86
88typedef enum {
89 AILSP_FAST, /* Fastest to get to */
90 AILSP_NEAR, /* Nearest to target */
91 AILSP_FAR, /* Farthest from target (within weapon's range) */
92 AILSP_DMG /* Best expected damage */
94
95typedef enum {
96 AILPW_RAND, /* Wander randomly */
97 AILPW_CW, /* Move clockwise */
98 AILPW_CCW /* Move counter-clockwise */
100/*
101 * Helper functions
102 */
103
110static const char* AIL_toTeamString (const int team)
111{
112 const char* teamStr = gi.GetConstVariable("luaaiteam", team);
113 if (teamStr == nullptr)
115 return teamStr;
116}
117
125static int AIL_toTeamInt (const char* team, const int param)
126{
127 int teamInt = TEAM_DEFAULT;
128 if (!gi.GetConstIntFromNamespace("luaaiteam", team, &teamInt))
130 return teamInt;
131}
132
139static ailVisType_t AIL_toVisInt (lua_State* L, const int index)
140{
141 int visInt = AILVT_ALL;
142 if (lua_isstring(L, index)) {
143 const char* s = lua_tostring(L, index);
144 if (!gi.GetConstIntFromNamespace("luaaivis", s, &visInt))
146 } else
148 return static_cast<ailVisType_t> (visInt);
149}
150
157static ailSortCritType_t AIL_toSortInt (lua_State* L, const int index)
158{
159 int sortInt = AILSC_DIST;
160 if (lua_isstring(L, index)) {
161 const char* s = lua_tostring(L, index);
162 if (!gi.GetConstIntFromNamespace("luaaisort", s, &sortInt))
164 } else
166 return static_cast<ailSortCritType_t> (sortInt);
167}
168
175static ailSortCritType_t AIL_toDistInt (lua_State* L, const int index)
176{
177 int distInt = AILSC_DIST;
178 if (lua_isstring(L, index)) {
179 const char* s = lua_tostring(L, index);
180 if (!gi.GetConstIntFromNamespace("luaaidist", s, &distInt))
182 } else
184 return static_cast<ailSortCritType_t> (distInt);
185}
186
193static ailShootPosType_t AIL_toShotPInt (lua_State* L, const int index)
194{
195 int spInt = AILSP_FAST;
196 if (lua_isstring(L, index)) {
197 const char* s = lua_tostring(L, index);
198 if (!gi.GetConstIntFromNamespace("luaaishot", s, &spInt))
200 } else
202 return static_cast<ailShootPosType_t> (spInt);
203}
204
211static ailWanderPosType AIL_toWanderPInt (lua_State* L, const int index)
212{
213 int wpInt = AILPW_RAND;
214 if (lua_isstring(L, index)) {
215 const char* s = lua_tostring(L, index);
216 if (!gi.GetConstIntFromNamespace("luaaiwander", s, &wpInt))
218 } else
220 return static_cast<ailWanderPosType> (wpInt);
221}
222
226typedef struct aiActor_s {
228} aiActor_t;
229
230
231/* Table sorting */
232template<typename T>
236};
237
238template<typename T>
240 return (i.sortLookup < j.sortLookup);
241}
242
243/*
244 * Current AI Actor.
245 */
246static Actor* AIL_ent;
247static Player* AIL_player;
248
249static float AIL_GetBestShot(const Actor& shooter, const Actor& target, const int tu, const float dist, shoot_types_t& bestType, fireDefIndex_t& bestFd, int& bestShots)
250{
251 int shotChecked = NONE;
252 float bestDmg = 0.0f;
253 bestShots = bestType = bestFd = NONE;
254 for (shoot_types_t shootType = ST_RIGHT; shootType < ST_NUM_SHOOT_TYPES; shootType++) {
255 const Item* item = AI_GetItemForShootType(shootType, AIL_ent);
256 if (item == nullptr)
257 continue;
258
259 const fireDef_t* fdArray = item->getFiredefs();
260 if (fdArray == nullptr)
261 continue;
262
263 for (fireDefIndex_t fdIdx = 0; fdIdx < item->ammoDef()->numFiredefs[fdArray->weapFdsIdx]; fdIdx++) {
264 const fireDef_t* fd = &fdArray[fdIdx];
265 const int time = G_ActorGetModifiedTimeForFiredef(AIL_ent, fd, false);
266 /* how many shoots can this actor do */
267 const int shots = tu / time;
268
269 if (!shots)
270 continue;
271
272 if (!AI_FighterCheckShoot(AIL_ent, &target, fd, dist))
273 continue;
274
275 const int shotFlags = fd->gravity | (fd->launched << 1) | (fd->rolled << 2);
276 if (shotChecked != shotFlags) {
277 shotChecked = shotFlags;
278 if (!AI_CheckLineOfFire(AIL_ent, &target, fd, shots))
279 continue;
280 }
281
282 /* Check if we can do the most damage here */
283 float dmg = AI_CalcShotDamage(AIL_ent, &target, fd, shootType) * shots;
284 if (dmg > bestDmg) {
285 bestDmg = dmg;
286 bestShots = shots;
287 bestFd = fdIdx;
288 bestType = shootType;
289 }
290 }
291 }
292
293 return bestDmg;
294}
295
296/*
297 * Actor metatable.
298 */
299
300/* Internal functions. */
301static int actorL_register(lua_State* L);
302static int lua_isactor(lua_State* L, int index);
303static aiActor_t* lua_toactor(lua_State* L, int index);
304static aiActor_t* lua_pushactor(lua_State* L, aiActor_t* actor);
305
306/* Metatable functions. */
307static int actorL_tostring(lua_State* L);
308static int actorL_pos(lua_State* L);
309static int actorL_shoot(lua_State* L);
310static int actorL_team(lua_State* L);
311static int actorL_throwgrenade(lua_State* L);
312static int actorL_TU(lua_State* L);
313static int actorL_HP(lua_State* L);
314static int actorL_morale(lua_State* L);
315static int actorL_isinjured(lua_State* L);
316static int actorL_isarmed(lua_State* L);
317static int actorL_isdead(lua_State* L);
318static int actorL_isvalidtarget(lua_State* L);
319
321static const luaL_Reg actorL_methods[] = {
322 {"__tostring", actorL_tostring},
323 {"pos", actorL_pos},
324 {"shoot", actorL_shoot},
325 {"team", actorL_team},
326 {"throwgrenade", actorL_throwgrenade},
327 {"TU", actorL_TU},
328 {"HP", actorL_HP},
329 {"morale", actorL_morale},
330 {"isinjured", actorL_isinjured},
331 {"isarmed", actorL_isarmed},
332 {"isdead", actorL_isdead},
333 {"isvalidtarget", actorL_isvalidtarget},
334 {nullptr, nullptr}
335};
336
340
341/* Internal functions. */
342static int pos3L_register(lua_State* L);
343static int lua_ispos3(lua_State* L, int index);
344static pos3_t* lua_topos3(lua_State* L, int index);
345static pos3_t* lua_pushpos3(lua_State* L, pos3_t* pos);
346
347/* Metatable functions. */
348static int pos3L_tostring(lua_State* L);
349static int pos3L_goto(lua_State* L);
350static int pos3L_face(lua_State* L);
351static int pos3L_distance(lua_State* L);
352
354static const luaL_Reg pos3L_methods[] = {
355 {"__tostring", pos3L_tostring},
356 {"go", pos3L_goto},
357 {"face", pos3L_face},
358 {"distance", pos3L_distance},
359 {nullptr, nullptr}
360};
361
362
366static int AIL_print(lua_State* L);
367static int AIL_squad(lua_State* L);
368static int AIL_select(lua_State* L);
369static int AIL_see(lua_State* L);
370static int AIL_crouch(lua_State* L);
371static int AIL_reactionfire(lua_State* L);
372static int AIL_roundsleft(lua_State* L);
373static int AIL_canreload(lua_State* L);
374static int AIL_reload(lua_State* L);
375static int AIL_positionshoot(lua_State* L);
376static int AIL_positionhide(lua_State* L);
377static int AIL_positionherd(lua_State* L);
378static int AIL_positionapproach(lua_State* L);
379static int AIL_grabweapon(lua_State* L);
380static int AIL_missiontargets(lua_State* L);
381static int AIL_waypoints(lua_State* L);
382static int AIL_positionmission(lua_State* L);
383static int AIL_positionwander(lua_State* L);
384static int AIL_findweapons(lua_State* L);
385static int AIL_isfighter(lua_State* L);
386static int AIL_setwaypoint(lua_State* L);
387static int AIL_difficulty(lua_State* L);
388static int AIL_positionflee(lua_State* L);
389static int AIL_weapontype(lua_State* L);
390static int AIL_actor(lua_State* L);
391static int AIL_tusforshooting(lua_State* L);
392static int AIL_class(lua_State* L);
393static int AIL_hideneeded(lua_State* L);
394
398static const luaL_Reg AIL_methods[] = {
399 {"print", AIL_print},
400 {"squad", AIL_squad},
401 {"select", AIL_select},
402 {"see", AIL_see},
403 {"crouch", AIL_crouch},
404 {"reactionfire", AIL_reactionfire},
405 {"roundsleft", AIL_roundsleft},
406 {"canreload", AIL_canreload},
407 {"reload", AIL_reload},
408 {"positionshoot", AIL_positionshoot},
409 {"positionhide", AIL_positionhide},
410 {"positionherd", AIL_positionherd},
411 {"positionapproach", AIL_positionapproach},
412 {"grabweapon", AIL_grabweapon},
413 {"missiontargets", AIL_missiontargets},
414 {"waypoints", AIL_waypoints},
415 {"positionmission", AIL_positionmission},
416 {"positionwander", AIL_positionwander},
417 {"findweapons", AIL_findweapons},
418 {"isfighter", AIL_isfighter},
419 {"setwaypoint", AIL_setwaypoint},
420 {"difficulty", AIL_difficulty},
421 {"positionflee", AIL_positionflee},
422 {"weapontype", AIL_weapontype},
423 {"actor", AIL_actor},
424 {"tusforshooting", AIL_tusforshooting},
425 {"class", AIL_class},
426 {"hideneeded", AIL_hideneeded},
427 {nullptr, nullptr}
428};
429
430
434
440static int actorL_register (lua_State* L)
441{
442 /* Create the metatable */
443 luaL_newmetatable(L, ACTOR_METATABLE);
444
445 /* Create the access table */
446 lua_pushvalue(L, -1);
447 lua_setfield(L, -2, "__index");
448
449 /* Register the values */
450 luaL_setfuncs(L, actorL_methods, 0);
451
452 /* Clean up stack. */
453 lua_pop(L, 1);
454
455 return 0; /* No error */
456}
457
464static int lua_isactor (lua_State* L, int index)
465{
466 if (lua_getmetatable(L, index) == 0)
467 return 0;
468 lua_getfield(L, LUA_REGISTRYINDEX, ACTOR_METATABLE);
469
470 int ret = 0;
471 if (lua_rawequal(L, -1, -2)) /* does it have the correct metatable? */
472 ret = 1;
473
474 lua_pop(L, 2); /* remove both metatables */
475 return ret;
476}
477
481static aiActor_t* lua_toactor (lua_State* L, int index)
482{
483 if (lua_isactor(L, index)) {
484 return (aiActor_t*) lua_touserdata(L, index);
485 }
486 luaL_typeerror(L, index, ACTOR_METATABLE);
487 return nullptr;
488}
489
493static aiActor_t* lua_pushactor (lua_State* L, aiActor_t* actor)
494{
495 aiActor_t* a = (aiActor_t*) lua_newuserdata(L, sizeof(aiActor_t));
496 *a = *actor;
497 luaL_getmetatable(L, ACTOR_METATABLE);
498 lua_setmetatable(L, -2);
499 return a;
500}
501
505static int actorL_tostring (lua_State* L)
506{
507 char buf[MAX_VAR];
508
509 assert(lua_isactor(L, 1));
510
511 const aiActor_t* target = lua_toactor(L, 1);
512 Com_sprintf(buf, sizeof(buf), "Actor( %s )", target->actor->chr.name);
513
514 lua_pushstring(L, buf);
515 return 1;
516}
517
521static int actorL_pos (lua_State* L)
522{
523 assert(lua_isactor(L, 1));
524
525 const aiActor_t* target = lua_toactor(L, 1);
526 lua_pushpos3(L, &target->actor->pos);
527 return 1;
528}
529
533static int actorL_shoot (lua_State* L)
534{
535 assert(lua_isactor(L, 1));
536
537 /* Target */
538 const aiActor_t* target = lua_toactor(L, 1);
539
540 /* Number of TU to spend shooting, fire mode will adjust to that. */
541 int tu = AIL_ent->getUsableTUs();
542 if (lua_gettop(L) > 1) {
543 assert(lua_isnumber(L, 2)); /* Must be a number. */
544
545 tu = std::min(static_cast<int>(lua_tonumber(L, 2)), tu);
546 }
547
548 const float dist = VectorDist(AIL_ent->origin, target->actor->origin);
549 shoot_types_t bestType = NONE;
550 fireDefIndex_t bestFd = NONE;
551 int bestShots = 0;
552 AIL_GetBestShot(*AIL_ent, *target->actor, tu, dist, bestType, bestFd, bestShots);
553
554 /* Failure - no weapon. */
555 if (bestType == NONE) {
556 lua_pushboolean(L, 0);
557 return 1;
558 }
559
560 bool shot = false;
561 while (bestShots > 0) {
562 if (G_IsDead(target->actor))
563 break;
564 bestShots--;
565 shot = G_ClientShoot(*AIL_player, AIL_ent, target->actor->pos, bestType, bestFd, nullptr, true, 0) || shot;
566 }
567
568 /* Success? */
569 lua_pushboolean(L, shot);
570 return 1;
571}
572
576static int actorL_team (lua_State* L)
577{
578 assert(lua_isactor(L, 1));
579
580 const aiActor_t* target = lua_toactor(L, 1);
581 assert(target != nullptr);
582 const char* team = AIL_toTeamString(target->actor->getTeam());
583 lua_pushstring(L, team);
584 return 1;
585}
586
590static int actorL_throwgrenade(lua_State* L)
591{
592 /* check parameter */
593 if (!(lua_gettop(L) && lua_isactor(L, 1))) {
595 lua_pushboolean(L, 0);
596 return 1;
597 }
598 const aiActor_t* target = lua_toactor(L, 1);
599 assert(target != nullptr);
600
601 /* Min number of enemies to use grenade */
602 int minNum = 0;
603 if (lua_gettop(L) > 1) {
604 if (!lua_isnumber(L, 2)) { /* Must be a number. */
606 lua_pushboolean(L, 0);
607 return 1;
608 }
609 minNum = static_cast<int>(lua_tonumber(L, 2));
610 }
611
612 /* Number of TU to spend */
613 int tus = AIL_ent->getUsableTUs();
614 if (lua_gettop(L) > 2) {
615 if (!lua_isnumber(L, 3)) { /* Must be a number. */
617 lua_pushboolean(L, 0);
618 return 1;
619 }
620 tus = std::min(static_cast<int>(lua_tonumber(L, 3)), tus);
621 }
622
623 /* Check that we have a free hand */
625 const Item* right = AIL_ent->getRightHandItem();
626 if (right)
627 hand = right->isHeldTwoHanded() || AIL_ent->getLeftHandItem() ? CID_MAX : CID_LEFT;
628 if (hand >= CID_MAX) {
629 lua_pushboolean(L, 0);
630 return 1;
631 }
632
633 /* Check if we have a grenade */
634 Item* grenade = nullptr;
635 const invDef_t* fromCont = AI_SearchGrenade(AIL_ent, &grenade);
636 if (!fromCont || !grenade) {
637 lua_pushboolean(L, 0);
638 return 1;
639 }
640 /* Now check if we can use it */
641 const fireDef_t* fdArray = grenade->getFiredefs();
642 const int invMoveCost = fromCont->out + INVDEF(hand)->in;
643 const shoot_types_t shotType = hand == CID_RIGHT ? ST_RIGHT : ST_LEFT;
644 float dist = VectorDist(AIL_ent->origin, target->actor->origin);
645 const fireDef_t* bestFd = nullptr;
646 for (fireDefIndex_t fdIdx = 0; fdIdx < grenade->ammoDef()->numFiredefs[fdArray->weapFdsIdx]; fdIdx++) {
647 const fireDef_t* fd = &fdArray[fdIdx];
648 const int time = invMoveCost + G_ActorGetModifiedTimeForFiredef(AIL_ent, fd, false);
649 /* Enough TU? */
650 if (time > tus)
651 continue;
652 /* In range? */
653 if (!AI_FighterCheckShoot(AIL_ent, target->actor, fd, dist))
654 continue;
655 /* LOF? */
656 if (!AI_CheckLineOfFire(AIL_ent, target->actor, fd, 1))
657 continue;
658
659 /* Select the first usable firemode */
660 bestFd = fd;
661 break;
662 }
663 if (!bestFd) {
664 lua_pushboolean(L, 0);
665 return 1;
666 }
667
668 /* Finally check if we want to use it now */
669 if (bestFd->splrad > 0) {
670 Actor* check = nullptr;
671 int n = 0;
672 while ((check = G_EdictsGetNextLivingActor(check))) {
673 /* check for distance */
674 dist = VectorDist(target->actor->origin, check->origin);
675 dist = dist > UNIT_SIZE / 2 ? dist - UNIT_SIZE / 2 : 0;
676 if (dist > bestFd->splrad)
677 continue;
678
679 if (!AI_IsHostile(AIL_ent, target->actor)) {
680 lua_pushboolean(L, 0);
681 return 1;
682 }
683 ++n;
684 }
685 /* Check there's large enough group of targets */
686 if (n < minNum) {
687 lua_pushboolean(L, 0);
688 return 1;
689 }
690 }
691
692 /* Try to move the grenade to the free hand */
693 if(!G_ActorInvMove(AIL_ent, fromCont, grenade, INVDEF(hand), NONE, NONE, true)) {
694 lua_pushboolean(L, 0);
695 return 1;
696 }
697 /* All right use it! */
698 const bool result = G_ClientShoot(*AIL_player, AIL_ent, target->actor->pos, shotType, bestFd->fdIdx, nullptr, true, 0);
699
700 lua_pushboolean(L, result);
701 return 1;
702}
703
707static int actorL_TU (lua_State* L)
708{
709 /* check parameter */
710 if (!(lua_gettop(L) && lua_isactor(L, 1))) {
712 lua_pushboolean(L, 0);
713 return 1;
714 }
715 const aiActor_t* actor = lua_toactor(L, 1);
716 assert(actor != nullptr);
717
718 lua_pushnumber(L, actor->actor->getUsableTUs());
719 return 1;
720}
721
725static int actorL_HP (lua_State* L)
726{
727 /* check parameter */
728 if (!(lua_gettop(L) && lua_isactor(L, 1))) {
730 lua_pushboolean(L, 0);
731 return 1;
732 }
733 const aiActor_t* actor = lua_toactor(L, 1);
734 assert(actor != nullptr);
735
736 lua_pushnumber(L, actor->actor->HP);
737 return 1;
738}
739
743static int actorL_morale (lua_State* L)
744{
745 /* check parameter */
746 if (!(lua_gettop(L) && lua_isactor(L, 1))) {
748 lua_pushboolean(L, 0);
749 return 1;
750 }
751 const aiActor_t* actor = lua_toactor(L, 1);
752 assert(actor != nullptr);
753
754 const char* morStat = "normal";
755 if (actor->actor->isPanicked())
756 morStat = "panic";
757 else if (actor->actor->isInsane())
758 morStat = "insane";
759 else if (actor->actor->isRaged())
760 morStat = "rage";
761 else if (actor->actor->getMorale() <= mor_brave->integer)
762 morStat = "cower";
763
764 lua_pushstring(L, morStat);
765 return 1;
766}
767
771static int actorL_isinjured (lua_State* L)
772{
773 /* check parameter */
774 if (!(lua_gettop(L) && lua_isactor(L, 1))) {
776 lua_pushboolean(L, 0);
777 return 1;
778 }
779 const aiActor_t* actor = lua_toactor(L, 1);
780 assert(actor != nullptr);
781
782 lua_pushboolean(L, G_IsActorWounded(actor->actor, true)
783 || actor->actor->HP <= actor->actor->chr.maxHP * 0.5);
784 return 1;
785}
786
790static int actorL_isarmed (lua_State* L)
791{
792 /* check parameter */
793 if (!(lua_gettop(L) && lua_isactor(L, 1))) {
795 lua_pushboolean(L, 0);
796 return 1;
797 }
798 const aiActor_t* actor = lua_toactor(L, 1);
799 assert(actor != nullptr);
800
801 lua_pushboolean(L, actor->actor->getRightHandItem() ? 1 : 0);
802 lua_pushboolean(L, actor->actor->getLeftHandItem() ? 1 : 0);
803 return 2;
804}
805
809static int actorL_isdead (lua_State* L)
810{
811 /* check parameter */
812 if (!(lua_gettop(L) && lua_isactor(L, 1))) {
814 lua_pushboolean(L, 0);
815 return 1;
816 }
817 const aiActor_t* actor = lua_toactor(L, 1);
818 assert(actor != nullptr);
819
820 lua_pushboolean(L, actor->actor->isDead());
821 return 1;
822}
823
827static int actorL_isvalidtarget (lua_State* L)
828{
829 assert(lua_isactor(L, 1));
830
831 const aiActor_t* target = lua_toactor(L, 1);
832 assert(target != nullptr);
833 lua_pushboolean(L, AI_IsHostile(AIL_ent, target->actor));
834 return 1;
835}
836
837
841
847static int pos3L_register (lua_State* L)
848{
849 /* Create the metatable */
850 luaL_newmetatable(L, POS3_METATABLE);
851
852 /* Create the access table */
853 lua_pushvalue(L, -1);
854 lua_setfield(L, -2, "__index");
855
856 /* Register the values */
857 luaL_setfuncs(L, pos3L_methods, 0);
858
859 /* Clean up the stack. */
860 lua_pop(L, 1);
861
862 return 0; /* No error */
863}
864
871static int lua_ispos3 (lua_State* L, int index)
872{
873 if (lua_getmetatable(L, index) == 0)
874 return 0;
875 lua_getfield(L, LUA_REGISTRYINDEX, POS3_METATABLE);
876
877 int ret = 0;
878 if (lua_rawequal(L, -1, -2)) /* does it have the correct metatable? */
879 ret = 1;
880
881 lua_pop(L, 2); /* remove both metatables */
882 return ret;
883}
884
888static pos3_t* lua_topos3 (lua_State* L, int index)
889{
890 if (lua_ispos3(L, index)) {
891 return (pos3_t*) lua_touserdata(L, index);
892 }
893 luaL_typeerror(L, index, POS3_METATABLE);
894 return nullptr;
895}
896
900static pos3_t* lua_pushpos3 (lua_State* L, pos3_t* pos)
901{
902 pos3_t* p = (pos3_t*) lua_newuserdata(L, sizeof(pos3_t));
903 memcpy(p, pos, sizeof(*p));
904 luaL_getmetatable(L, POS3_METATABLE);
905 lua_setmetatable(L, -2);
906 return p;
907}
908
912static int pos3L_tostring (lua_State* L)
913{
914 char buf[MAX_VAR];
915
916 assert(lua_ispos3(L, 1));
917
918 const pos3_t* p = lua_topos3(L, 1);
919 Com_sprintf(buf, sizeof(buf), "Pos3( x=%d, y=%d, z=%d )", (*p)[0], (*p)[1], (*p)[2]);
920
921 lua_pushstring(L, buf);
922 return 1;
923}
924
928static int pos3L_goto (lua_State* L)
929{
930 assert(lua_ispos3(L, 1));
931
932 /* Calculate move table. */
933 G_MoveCalc(0, AIL_ent, AIL_ent->pos, AIL_ent->getUsableTUs());
934 gi.MoveStore(level.pathingMap);
935
936 /* Move. */
937 const pos3_t* pos = lua_topos3(L, 1);
938 /* do the move */
939 for (;;) {
940 if (AIL_ent->isDead())
941 break;
942 G_ClientMove(*AIL_player, 0, AIL_ent, *pos);
943 if (AIL_ent->isSamePosAs(*pos))
944 break;
945 const pos_t length = G_ActorMoveLength(AIL_ent, level.pathingMap, *pos, false);
946 if (length > AIL_ent->getUsableTUs() || length >= ROUTING_NOT_REACHABLE)
947 break;
948 }
949
950 lua_pushboolean(L, AIL_ent->isSamePosAs(*pos));
951 return 1;
952}
953
957static int pos3L_face (lua_State* L)
958{
959 assert(lua_ispos3(L, 1));
960
961 const pos3_t* pos = lua_topos3(L, 1);
963
964 lua_pushboolean(L, 1);
965 return 1;
966}
967
971static int pos3L_distance (lua_State* L)
972{
973 assert(lua_ispos3(L, 1));
974
975 pos3_t* pos = lua_topos3(L, 1);
976 assert(pos != nullptr);
977
978 ailSortCritType_t distType = AILSC_DIST;
979 if (lua_gettop(L) > 1)
980 distType = AIL_toDistInt(L, 2);
981
982 switch (distType) {
983 case AILSC_PATH:
984 /* Find a path to the target pos */
985 if (!G_FindPath(0, AIL_ent, AIL_ent->pos, *pos, AIL_ent->isCrouched(), ROUTING_NOT_REACHABLE - 1)) {
986 lua_pushnumber(L, ROUTING_NOT_REACHABLE);
987 return 1;
988 }
989 lua_pushnumber(L, G_ActorMoveLength(AIL_ent, level.pathingMap, *pos, false));
990 return 1;
991 case AILSC_DIST:
992 default:
993 vec3_t to;
994 PosToVec(*pos, to);
995 lua_pushnumber(L, VectorDist(AIL_ent->origin, to));
996 return 1;
997 }
998}
999
1003/*
1004 * General functions.
1005 */
1006
1010static int AIL_print (lua_State* L)
1011{
1012 const int n = lua_gettop(L); /* number of arguments */
1013
1014 for (int i = 1; i <= n; i++) {
1015 const char* s;
1016 bool meta = false;
1017
1018 lua_pushvalue(L, i); /* value to print */
1019 if (luaL_callmeta(L, -1, "__tostring")) {
1020 s = lua_tostring(L, -1);
1021 meta = true;
1022 } else {
1023 switch (lua_type(L, -1)) {
1024 case LUA_TNUMBER:
1025 case LUA_TSTRING:
1026 s = lua_tostring(L, -1);
1027 break;
1028 case LUA_TBOOLEAN:
1029 s = lua_toboolean(L, -1) ? "true" : "false";
1030 break;
1031 case LUA_TNIL:
1032 s = "nil";
1033 break;
1034
1035 default:
1036 s = "unknown lua type";
1037 break;
1038 }
1039 }
1040 gi.DPrintf("%s%s", (i > 1) ? "\t" : "", s);
1041 lua_pop(L, 1); /* Pop the value */
1042 if (meta) /* Meta creates an additional string. */
1043 lua_pop(L, 1);
1044 }
1045
1046 gi.DPrintf("\n");
1047 return 0;
1048}
1049
1050/*
1051 * Player functions.
1052 */
1053
1057static int AIL_squad (lua_State* L)
1058{
1059 if (g_ailua->integer < 2) {
1060 gi.DPrintf("Problem while running lua: attempt to get the player's team while not in team mode.");
1061 lua_pushnil(L);
1062 return 1;
1063 }
1064
1065 /* New Lua table. */
1066 lua_newtable(L);
1067
1068 int i = 1; /* LUA indexes starting from one */
1069 Actor* check = nullptr;
1070 while ((check = G_EdictsGetNextActor(check))) {
1071 if (check->getPlayerNum() != AIL_player->getNum())
1072 continue;
1073 lua_pushnumber(L, i++); /* index */
1074 aiActor_t target;
1075 target.actor = check;
1076 lua_pushactor(L, &target); /* value */
1077 lua_rawset(L, -3); /* store the value in the table */
1078 }
1079 return 1; /* Returns the table of actors. */
1080}
1081
1085static int AIL_select (lua_State* L)
1086{
1087 if (g_ailua->integer < 2) {
1088 gi.DPrintf("Problem while running lua: attempt to select the active AI actor while not in team mode.");
1089 lua_pushnil(L);
1090 } else if (lua_gettop(L) > 0 && lua_isactor(L, 1)) {
1091 aiActor_t* target = lua_toactor(L, 1);
1092 if (target->actor->getPlayerNum() == AIL_player->getNum())
1093 AIL_ent = target->actor;
1094 lua_pushboolean(L, AIL_ent == target->actor);
1095 } else {
1097 lua_pushboolean(L, false);
1098 }
1099 return 1;
1100}
1101
1102/*
1103 * Actor functions
1104 */
1105
1109static int AIL_see (lua_State* L)
1110{
1111 /* Defaults. */
1112 int team = TEAM_ALL;
1113 ailVisType_t vision = AILVT_ALL;
1114 ailSortCritType_t sortCrit = AILSC_DIST;
1115 bool invTeam = false;
1116
1117 /* Handle parameters. */
1118 if ((lua_gettop(L) > 0)) {
1119 /* Get what to "see" with. */
1120 vision = AIL_toVisInt(L, 1);
1121
1122 /* We now check for different teams. */
1123 if ((lua_gettop(L) > 1)) {
1124 if (lua_isstring(L, 2)) {
1125 const char* s = lua_tostring(L, 2);
1126 if (s[0] == '-' || s[0] == '~') {
1127 invTeam = true;
1128 ++s;
1129 }
1130 team = AIL_toTeamInt(s, 2);
1131 /* Trying to see no one? */
1132 if (team == TEAM_ALL && invTeam)
1134 } else
1136 }
1137
1138 /* Sorting criteria */
1139 if ((lua_gettop(L) > 2)) {
1140 sortCrit = AIL_toSortInt(L, 3);
1141 }
1142 }
1143
1144 int n = 0;
1145 Actor* check = nullptr;
1147 /* Get visible things. */
1148 const int visDist = G_VisCheckDist(AIL_ent);
1149 /* We are about to check the team view, update it accordingly */
1150 if (vision == AILVT_TEAM)
1152 while ((check = G_EdictsGetNextLivingActor(check))) {
1153 if (AIL_ent == check)
1154 continue;
1155 const float distance = VectorDistSqr(AIL_ent->pos, check->pos);
1156 /* Check for team match if needed. */
1157 if ((team == TEAM_ALL || (check->getTeam() == team ? !invTeam : invTeam))
1158 && (vision == AILVT_ALL
1159 || (vision == AILVT_SIGHT && G_Vis(AIL_ent->getTeam(), AIL_ent, check, VT_NOFRUSTUM))
1160 || (vision == AILVT_TEAM && G_IsVisibleForTeam(check, AIL_ent->getTeam()))
1161 || (vision == AILVT_DIST && distance <= visDist * visDist))) {
1162 switch (sortCrit) {
1163 case AILSC_PATH:
1164 {
1166 if (G_FindPath(0, AIL_ent, AIL_ent->pos, check->pos, false, 0xFE))
1167 move = gi.MoveLength(level.pathingMap, check->pos, 0, false);
1168 sortTable[n].sortLookup = move;
1169 }
1170 break;
1171 case AILSC_HP:
1172 sortTable[n].sortLookup = check->HP;
1173 break;
1174 case AILSC_DIST:
1175 default:
1176 sortTable[n].sortLookup = VectorDistSqr(AIL_ent->pos, check->pos);
1177 break;
1178 }
1179 sortTable[n++].data = check;
1180 }
1181 }
1182
1183 /* Sort by given criterion - lesser first. */
1184 std::sort(sortTable, sortTable + n);
1185
1186 /* Now save it in a Lua table. */
1187 lua_newtable(L);
1188 for (int i = 0; i < n; i++) {
1189 lua_pushnumber(L, i + 1); /* index, starts with 1 */
1190 aiActor_t target;
1191 target.actor = sortTable[i].data;
1192 lua_pushactor(L, &target); /* value */
1193 lua_rawset(L, -3); /* store the value in the table */
1194 }
1195 return 1; /* Returns the table of actors. */
1196}
1197
1201static int AIL_crouch (lua_State* L)
1202{
1203 if (lua_gettop(L) > 0) {
1204 if (lua_isboolean(L, 1)) {
1205 const bool reqState = lua_toboolean(L, 1);
1206 const bool state = AIL_ent->isCrouched();
1207 if (reqState != state)
1209 } else
1211 }
1212
1213 lua_pushboolean(L, AIL_ent->isCrouched());
1214 return 1;
1215}
1216
1220static int AIL_reactionfire (lua_State* L)
1221{
1222 if (lua_gettop(L) > 0) {
1223 int reactionState = 0;
1224
1225 if (lua_isstring(L, 1)) {
1226 /* get reaction fire mode */
1227 const char* cmd = lua_tostring(L, 1);
1228 reactionState = Q_streq(cmd, "disable") ? ~STATE_REACTION : STATE_REACTION;
1229 }
1230
1231 if (reactionState) {
1232 G_ClientStateChange(*AIL_player, AIL_ent, reactionState, true);
1233 } else {
1235 }
1236 }
1237
1238 lua_pushboolean(L, AIL_ent->isReaction());
1239 return 1;
1240}
1241
1245static int AIL_roundsleft (lua_State* L)
1246{
1247 /* Right hand */
1248 const Item* rightHand = AIL_ent->getRightHandItem();
1249 if (rightHand && (rightHand->def()->ammo < 1 || rightHand->getAmmoLeft() > 0))
1250 lua_pushnumber(L, rightHand->getAmmoLeft());
1251 else
1252 /* Currently unusable */
1253 lua_pushnil(L);
1254
1255 /* Left hand */
1256 const Item* leftHand = AIL_ent->getLeftHandItem();
1257 if (leftHand && (leftHand->def()->ammo < 1 || leftHand->getAmmoLeft() > 0))
1258 lua_pushnumber(L, leftHand->getAmmoLeft());
1259 else
1260 lua_pushnil(L);
1261 return 2;
1262}
1263
1267static int AIL_canreload (lua_State* L)
1268{
1269 lua_pushboolean(L, G_ClientCanReload(AIL_ent, CID_RIGHT));
1270 lua_pushboolean(L, G_ClientCanReload(AIL_ent, CID_LEFT));
1271 return 2;
1272}
1273
1277static int AIL_reload (lua_State* L)
1278{
1279 containerIndex_t container = CID_RIGHT; /* Default to right hand. */
1280
1281 if (lua_gettop(L) > 0) {
1282 if (lua_isstring(L, 1)) {
1283 const char* s = lua_tostring(L, 1);
1284
1285 if (Q_streq(s, "right")) {
1286 container = CID_RIGHT;
1287 } else if (Q_streq(s, "left")) {
1288 container = CID_LEFT;
1289 } else {
1291 return 0;
1292 }
1293 } else {
1295 return 0;
1296 }
1297 }
1298
1299 AI_TryToReloadWeapon(AIL_ent, container);
1300 return 0;
1301}
1302
1306static int AIL_grabweapon (lua_State* L)
1307{
1308 lua_pushboolean(L, G_ClientGetWeaponFromInventory(AIL_ent));
1309 return 1;
1310}
1311
1315static int AIL_positionshoot (lua_State* L)
1316{
1317 /* We need a target. */
1318 assert(lua_isactor(L, 1));
1319 aiActor_t* target = lua_toactor(L, 1);
1320
1321 /* Make things more simple. */
1322 Actor* actor = AIL_ent;
1323
1324 /* Shooting strategy */
1325 ailShootPosType_t posType = AILSP_FAST;
1326 if ((lua_gettop(L) > 1))
1327 posType = AIL_toShotPInt(L, 2);
1328
1329 /* Number of TU to spend shooting, to make sure we have enough tus to actually fire. */
1330 int tus = actor->getUsableTUs();
1331 if (lua_gettop(L) > 2) {
1332 assert(lua_isnumber(L, 3)); /* Must be a number. */
1333
1334 tus = std::min(static_cast<int>(lua_tonumber(L, 3)), tus);
1335 }
1336
1337 /* Don't shoot units under our control */
1338 if (!AI_IsHostile(actor, target->actor)) {
1339 lua_pushboolean(L, 0);
1340 return 1;
1341 }
1342
1343 shoot_types_t shootType = ST_RIGHT;
1344 const Item* item = AI_GetItemForShootType(shootType, AIL_ent);
1345 if (item == nullptr) {
1346 shootType = ST_LEFT;
1347 item = AI_GetItemForShootType(shootType, AIL_ent);
1348 }
1349
1350 /* Check for weapon. */
1351 if (item == nullptr) {
1352 lua_pushboolean(L, 0);
1353 return 1;
1354 }
1355 const fireDef_t* fd = item->getFastestFireDef();
1356 if (fd == nullptr) {
1357 lua_pushboolean(L, 0);
1358 return 1;
1359 }
1360
1361 int fdTime = G_ActorGetModifiedTimeForFiredef(AIL_ent, fd, false);
1362 if (tus - fdTime <= 0) {
1363 lua_pushboolean(L, 0);
1364 return 1;
1365 }
1366
1367 /* Calculate move table. */
1368 G_MoveCalc(0, actor, actor->pos, tus);
1369 gi.MoveStore(level.pathingMap);
1370
1371 /* set borders */
1372 const int rad = (tus + 1) / TU_MOVE_STRAIGHT;
1373
1374 pos3_t oldPos;
1375 vec3_t oldOrigin;
1376 VectorCopy(actor->pos, oldPos);
1377 VectorCopy(actor->origin, oldOrigin);
1378
1379 /* evaluate moving to every possible location in the search area,
1380 * including combat considerations */
1381 float bestScore = 0.0f;
1382 pos3_t to, bestPos;
1383 VectorSet(bestPos, 0, 0, PATHFINDING_HEIGHT);
1384 AiAreaSearch searchArea(oldPos, rad);
1385 while (searchArea.getNext(to)) {
1386 actor->setOrigin(to);
1387 const pos_t move = G_ActorMoveLength(actor, level.pathingMap, to, true);
1388 if (move > tus || move == ROUTING_NOT_REACHABLE)
1389 continue;
1390 if (!AI_CheckPosition(actor, actor->pos))
1391 continue;
1392 /* Can we see the target? */
1393 if (!G_IsVisibleForTeam(target->actor, actor->getTeam()) && G_ActorVis(actor, target->actor, true) < ACTOR_VIS_10)
1394 continue;
1395
1396 const float dist = VectorDist(actor->origin, target->actor->origin);
1397 int dummy = NONE;
1398 const float bestDmg = AIL_GetBestShot(*actor, *target->actor, tus - move, dist, dummy, dummy, dummy);
1399 if (dummy == NONE)
1400 continue;
1401
1402 float score;
1403 switch (posType) {
1404 case AILSP_NEAR:
1405 score = -dist;
1406 break;
1407 case AILSP_FAR:
1408 score = dist;
1409 break;
1410 case AILSP_DMG:
1411 score = bestDmg;
1412 break;
1413 case AILSP_FAST:
1414 default:
1415 score = -move;
1416 break;
1417 }
1418 if (score > bestScore || bestPos[2] >= PATHFINDING_HEIGHT) {
1419 VectorCopy(to, bestPos);
1420 bestScore = score;
1421 }
1422 }
1423
1424 VectorCopy(oldPos, actor->pos);
1425 VectorCopy(oldOrigin, actor->origin);
1426
1427 /* No position found in range. */
1428 if (bestPos[2] >= PATHFINDING_HEIGHT) {
1429 lua_pushboolean(L, 0);
1430 return 1;
1431 }
1432
1433 /* Return the spot. */
1434 lua_pushpos3(L, &bestPos);
1435 return 1;
1436}
1437
1443static int AIL_positionhide (lua_State* L)
1444{
1445 int hidingTeam = AI_GetHidingTeam(AIL_ent);
1446
1447 /* parse parameter */
1448 if (lua_gettop(L)) {
1449 if (lua_isstring(L, 1)) {
1450 const char* s = lua_tostring(L, 1);
1451 bool invTeam = false;
1452 if (s[0] == '-' || s[0] == '~') {
1453 invTeam = true;
1454 ++s;
1455 }
1456 const int team = AIL_toTeamInt(s, 1);
1457 if (team == TEAM_ALL)
1459 else if (invTeam)
1460 hidingTeam = -team;
1461 else
1462 hidingTeam = team;
1463 } else {
1465 }
1466 }
1467
1468 int tus = AIL_ent->getUsableTUs();
1469 /* parse parameter */
1470 if (lua_gettop(L) > 1) {
1471 if (lua_isnumber(L, 2)) {
1472 tus = std::min(static_cast<int>(lua_tonumber(L, 2)), tus);
1473 } else {
1475 }
1476 }
1477
1478 pos3_t save;
1479 VectorCopy(AIL_ent->pos, save);
1480
1481 if (AI_FindHidingLocation(hidingTeam, AIL_ent, AIL_ent->pos, tus)) {
1482 /* Return the spot. */
1483 lua_pushpos3(L, &AIL_ent->pos);
1484 } else {
1485 lua_pushboolean(L, 0);
1486 }
1487 AIL_ent->setOrigin(save);
1488 return 1;
1489}
1490
1499static int AIL_positionherd (lua_State* L)
1500{
1501 /* check parameter */
1502 if (!(lua_gettop(L) && lua_isactor(L, 1))) {
1504 lua_pushboolean(L, 0);
1505 return 1;
1506 }
1507 const aiActor_t* target = lua_toactor(L, 1);
1508
1509 int tus = AIL_ent->getUsableTUs();
1510 /* parse parameter */
1511 if (lua_gettop(L) > 1) {
1512 if (lua_isnumber(L, 2)) {
1513 tus = std::min(static_cast<int>(lua_tonumber(L, 2)), tus);
1514 } else {
1516 }
1517 }
1518
1519 bool inverse = false;
1520 if (lua_gettop(L) > 2) {
1521 if (lua_isboolean(L, 3))
1522 inverse = lua_toboolean(L, 3);
1523 else
1525 }
1526
1527 pos3_t save;
1528 VectorCopy(AIL_ent->pos, save);
1529 if (AI_FindHerdLocation(AIL_ent, AIL_ent->pos, target->actor->origin, tus, inverse)) {
1530 lua_pushpos3(L, &AIL_ent->pos);
1531 } else {
1532 lua_pushboolean(L, 0);
1533 }
1534 AIL_ent->setOrigin(save);
1535 return 1;
1536}
1537
1541static int AIL_positionapproach (lua_State* L)
1542{
1543 /* check parameter */
1544 if (!(lua_gettop(L) && lua_ispos3(L, 1))) {
1546 lua_pushboolean(L, 0);
1547 return 1;
1548 }
1549
1550 const pos3_t* target = lua_topos3(L, 1);
1551 assert(target != nullptr);
1552
1553 int tus = AIL_ent->getUsableTUs();
1554 if (lua_gettop(L) > 1) {
1555 if (lua_isnumber(L, 2))
1556 tus = std::min(static_cast<int>(lua_tonumber(L, 2)), tus);
1557 else
1559 }
1560
1561 bool hide = false;
1562 if (lua_gettop(L) > 2){
1563 if (lua_isboolean(L, 3))
1564 hide = lua_toboolean(L, 3);
1565 else
1567 }
1568
1569 /* Find a path to the target actor */
1570 const int maxTUs = ROUTING_NOT_REACHABLE - 1;
1571 pos3_t to;
1572 VectorCopy(*target, to);
1573 byte crouchingState = AIL_ent->isCrouched() ? 1 : 0;
1574 if (!G_FindPath(0, AIL_ent, AIL_ent->pos, to, crouchingState, maxTUs)) {
1575 /* Not found */
1576 lua_pushboolean(L, 0);
1577 return 1;
1578 }
1579
1580 /* Find the farthest we can go with current TUs */
1581 int dvec;
1582 while ((dvec = gi.MoveNext(level.pathingMap, to, crouchingState)) != ROUTING_UNREACHABLE) {
1583 /* Note: here we skip the first position so we don't try to walk into the target */
1584 PosSubDV(to, crouchingState, dvec);
1586 continue;
1587 if (!AI_CheckPosition(AIL_ent, to))
1588 continue;
1589 const byte length = G_ActorMoveLength(AIL_ent, level.pathingMap, to, false);
1590 if (length <= tus)
1591 break;
1592 /* We are going backwards to the origin. */
1593 }
1594
1595 if (AIL_ent->isSamePosAs(to))
1596 lua_pushboolean(L, 0);
1597 else
1598 lua_pushpos3(L, &to);
1599 return 1;
1600}
1601
1605static int AIL_missiontargets (lua_State* L)
1606{
1607
1608 /* Defaults. */
1609 int team = TEAM_ALL;
1610 ailVisType_t vision = AILVT_ALL;
1611 ailSortCritType_t sortCrit = AILSC_DIST;
1612 bool invTeam = false;
1613
1614 /* Handle parameters. */
1615 if ((lua_gettop(L) > 0)) {
1616 /* Get what to "see" with. */
1617 vision = AIL_toVisInt(L, 1);
1618
1619 /* We now check for different teams. */
1620 if ((lua_gettop(L) > 1)) {
1621 if (lua_isstring(L, 2)) {
1622 const char* s = lua_tostring(L, 2);
1623 if (s[0] == '-' || s[0] == '~') {
1624 invTeam = true;
1625 ++s;
1626 }
1627 team = AIL_toTeamInt(s, 2);
1628 /* Trying to see no one? */
1629 if (team == TEAM_ALL && invTeam)
1631 } else
1633 }
1634
1635 /* Sorting criteria */
1636 if ((lua_gettop(L) > 2))
1637 sortCrit = AIL_toDistInt(L, 3);
1638 }
1639
1640 int n = 0;
1642 /* Get visible things. */
1643 const int visDist = G_VisCheckDist(AIL_ent);
1644 Edict* mission = nullptr;
1645 while ((mission = G_EdictsGetNextInUse(mission))) {
1646 if (mission->type != ET_MISSION)
1647 continue;
1648 const float distance = VectorDistSqr(AIL_ent->pos, mission->pos);
1649 /* Check for team match if needed. */
1650 if ((team == TEAM_ALL || (mission->getTeam() == team ? !invTeam : invTeam))
1651 && (vision == AILVT_ALL
1652 || (vision == AILVT_SIGHT && !G_TestLineWithEnts(AIL_ent->origin, mission->origin))
1653 || (vision == AILVT_DIST && distance <= visDist * visDist))) {
1654 switch (sortCrit) {
1655 case AILSC_PATH:
1656 {
1658 if (G_FindPath(0, AIL_ent, AIL_ent->pos, mission->pos, false, ROUTING_NOT_REACHABLE - 1))
1659 move = gi.MoveLength(level.pathingMap, mission->pos, 0, false);
1660 sortTable[n].sortLookup = move;
1661 }
1662 break;
1663 case AILSC_DIST:
1664 default:
1665 sortTable[n].sortLookup = VectorDistSqr(AIL_ent->pos, mission->pos);
1666 break;
1667 }
1668 sortTable[n++].data = mission;
1669 }
1670 }
1671
1672 /* Sort by given criterion - lesser first. */
1673 std::sort(sortTable, sortTable + n);
1674
1675 /* Now save it in a Lua table. */
1676 lua_newtable(L);
1677 for (int i = 0; i < n; i++) {
1678 lua_pushnumber(L, i + 1); /* index, starts with 1 */
1679 lua_pushpos3(L, &sortTable[i].data->pos); /* value */
1680 lua_rawset(L, -3); /* store the value in the table */
1681 }
1682 return 1; /* Returns the table of positions. */
1683}
1684
1688static int AIL_waypoints (lua_State* L)
1689{
1690 const float minEnemyDist = 160.0f;
1691 /* Min distance to waypoint */
1692 float minDist = 800.0f;
1693 if (lua_gettop(L) > 0) {
1694 if (lua_isnumber(L, 1))
1695 minDist = lua_tonumber(L, 1);
1696 else
1698 }
1699
1700 /* Sorting criteria */
1701 ailSortCritType_t sortCrit = AILSC_DIST;
1702 if ((lua_gettop(L) > 1))
1703 sortCrit = AIL_toDistInt(L, 2);
1704
1705 int n = 0;
1707 for (Edict* checkPoint = level.ai_waypointList; checkPoint != nullptr; checkPoint = checkPoint->groupChain) {
1708 if (checkPoint->inuse)
1709 continue;
1710 if (checkPoint->getTeam() != AIL_ent->getTeam())
1711 continue;
1712 /* Don't walk to enemy ambush */
1713 Actor* check = nullptr;
1714 bool ambush = false;
1715 while ((check = G_EdictsGetNextLivingActorOfTeam(check, TEAM_ALIEN))) {
1716 const float dist = VectorDist(AIL_ent->origin, check->origin);
1717 /* @todo add visibility check here? */
1718 if (dist < minEnemyDist) {
1719 ambush = true;
1720 break;
1721 }
1722 }
1723 if (ambush)
1724 continue;
1725 switch (sortCrit) {
1726 case AILSC_PATH:
1727 {
1729 if (G_FindPath(0, AIL_ent, AIL_ent->pos, checkPoint->pos, false, ROUTING_NOT_REACHABLE - 1))
1730 move = gi.MoveLength(level.pathingMap, checkPoint->pos, 0, false);
1731 if (move < minDist * TU_MOVE_STRAIGHT)
1732 continue;
1733 if (checkPoint->count < AIL_ent->count) {
1734 sortTable[n].sortLookup = move;
1735 sortTable[++n].data = checkPoint;
1736 }
1737 }
1738 break;
1739 case AILSC_DIST:
1740 default:
1741 {
1742 const float dist = VectorDist(AIL_ent->origin, checkPoint->origin);
1743 if (dist < minDist * UNIT_SIZE)
1744 continue;
1745 if (checkPoint->count < AIL_ent->count) {
1746 sortTable[n].sortLookup = dist;
1747 sortTable[++n].data = checkPoint;
1748 }
1749 }
1750 break;
1751 }
1752 }
1753
1754 /* Sort by distance */
1755 std::sort(sortTable, sortTable + n);
1756
1757 /* Now save it in a Lua table. */
1758 lua_newtable(L);
1759 for (int i = 0; i < n; i++) {
1760 lua_pushnumber(L, i + 1); /* index, starts with 1 */
1761 lua_pushpos3(L, &sortTable[i].data->pos); /* value */
1762 lua_rawset(L, -3); /* store the value in the table */
1763 }
1764 return 1; /* Returns the table of positions. */
1765}
1766
1771static int AIL_positionmission (lua_State* L)
1772{
1773 /* check parameter */
1774 if (!(lua_gettop(L) && lua_ispos3(L, 1))) {
1776 lua_pushboolean(L, 0);
1777 return 1;
1778 }
1779 const pos3_t* target = lua_topos3(L, 1);
1780 int tus = AIL_ent->getUsableTUs();
1781 if (lua_gettop(L) > 1) {
1782 if (lua_isnumber(L, 2))
1783 tus = lua_tonumber(L, 2);
1784 else
1786 }
1787 G_MoveCalc(0, AIL_ent, AIL_ent->pos, tus);
1788 gi.MoveStore(level.pathingMap);
1789
1790 pos3_t oldPos;
1791 VectorCopy(AIL_ent->pos, oldPos);
1792 int radius = 3;
1793 const Edict* const mission = G_GetEdictFromPos(*target, ET_MISSION);
1794 if (mission)
1795 radius = mission->radius;
1796 if (AI_FindMissionLocation(AIL_ent, *target, tus, radius))
1797 lua_pushpos3(L, &AIL_ent->pos);
1798 else
1799 lua_pushboolean(L, 0);
1800
1801 AIL_ent->setOrigin(oldPos);
1802 return 1;
1803}
1804
1809static int AIL_positionwander (lua_State* L)
1810{
1811 /* Calculate move table. */
1812 G_MoveCalc(0, AIL_ent, AIL_ent->pos, AIL_ent->getUsableTUs());
1813 gi.MoveStore(level.pathingMap);
1814
1815 /* Set defaults */
1816 int radius = (AIL_ent->getUsableTUs() + 1) / TU_MOVE_STRAIGHT;
1817 pos3_t center;
1818 VectorCopy(AIL_ent->pos, center);
1819 int method = AILPW_RAND;
1820 int tus = AIL_ent->getUsableTUs();
1821
1822 /* Check parameters */
1823 if (lua_gettop(L) > 0)
1824 method = AIL_toWanderPInt(L, 1);
1825 if (lua_gettop(L) > 1) {
1826 if (lua_isnumber(L, 2))
1827 radius = lua_tonumber(L, 2);
1828 else
1830 }
1831 if (lua_gettop(L) > 2) {
1832 if (lua_ispos3(L, 3))
1833 VectorCopy(*lua_topos3(L, 3), center);
1834 else
1836 }
1837
1838 if (lua_gettop(L) > 3) {
1839 if (lua_isnumber(L, 4))
1840 tus = std::min(static_cast<int>(lua_tonumber(L, 4)), tus);
1841 else
1843 }
1844
1845 vec3_t d;
1846 if (method > 0)
1847 VectorSubtract(AIL_ent->pos, center, d);
1848 const int cDir = method > 0 ? (VectorEmpty(d) ? AIL_ent->dir : AngleToDir(static_cast<int>(atan2(d[1], d[0]) * todeg))) : NONE;
1849 float bestScore = 0;
1850 pos3_t bestPos = {0, 0, PATHFINDING_HEIGHT};
1851 pos3_t pos;
1852 AiAreaSearch searchArea(center, radius);
1853 while (searchArea.getNext(pos)) {
1854 const pos_t move = G_ActorMoveLength(AIL_ent, level.pathingMap, pos, true);
1855 if (move >= ROUTING_NOT_REACHABLE || move > tus)
1856 continue;
1857 if (!AI_CheckPosition(AIL_ent, pos))
1858 continue;
1859 float score = 0.0f;
1860 switch (method) {
1861 case AILPW_RAND:
1862 score = rand();
1863 break;
1864 case AILPW_CW:
1865 case AILPW_CCW: {
1866 score = VectorDistSqr(center, pos);
1867 VectorSubtract(pos, center, d);
1868 int dir = AngleToDir(static_cast<int>(atan2(d[1], d[0]) * todeg));
1869 if (!(method == AILPW_CW && dir == dvright[cDir]) && !(method == AILPW_CCW && dir == dvleft[cDir]))
1870 for (int n = 1; n < 8; ++n) {
1871 dir = method == 1 ? dvleft[dir] : dvright[dir];
1872 score /= pow(n * 2.0f, 2);
1873 if ((method == 1 && dir == dvright[cDir]) || (method == 2 && dir == dvleft[cDir]))
1874 break;
1875 }
1876 }
1877 break;
1878 }
1879 if (score > bestScore) {
1880 bestScore = score;
1881 VectorCopy(pos, bestPos);
1882 }
1883 }
1884
1885 if (bestPos[2] >= PATHFINDING_HEIGHT) {
1886 lua_pushboolean(L, 0);
1887 return 1;
1888 }
1889 lua_pushpos3(L, &bestPos);
1890 return 1;
1891}
1892
1896static int AIL_findweapons (lua_State* L)
1897{
1898 bool full = false;
1899 if (lua_gettop(L) > 0) {
1900 if (lua_isboolean(L, 1))
1901 full = lua_toboolean(L, 1);
1902 else
1904 }
1905
1907 int n = 0;
1908 Edict* check = nullptr;
1909 while ((check = G_EdictsGetNextInUse(check))) {
1910 if (check->type != ET_ITEM)
1911 continue;
1912 if(!AI_CheckPosition(AIL_ent, check->pos))
1913 continue;
1914 if (!G_FindPath(0, AIL_ent, AIL_ent->pos, check->pos, AIL_ent->isCrouched(), ROUTING_NOT_REACHABLE - 1))
1915 continue;
1916 const pos_t move = G_ActorMoveLength(AIL_ent, level.pathingMap, check->pos, false);
1917 if (full || move <= AIL_ent->getUsableTUs() - INVDEF(CID_FLOOR)->out - INVDEF(CID_RIGHT)->in) {
1918 for (const Item* item = check->getFloor(); item; item = item->getNext()) {
1920 if (item->isWeapon() && (item->getAmmoLeft() > 0 || item->def()->ammo <= 0)) {
1921 sortTable[n].data = check;
1922 sortTable[n++].sortLookup = move;
1923 break;
1924 }
1925 }
1926 }
1927 }
1928
1929 /* Sort by distance */
1930 std::sort(sortTable, sortTable + n);
1931
1932 /* Now save it in a Lua table. */
1933 lua_newtable(L);
1934 for (int i = 0; i < n; i++) {
1935 lua_pushnumber(L, i + 1); /* index, starts with 1 */
1936 lua_pushpos3(L, &sortTable[i].data->pos); /* value */
1937 lua_rawset(L, -3); /* store the value in the table */
1938 }
1939 return 1; /* Returns the table of positions. */
1940}
1941
1945static int AIL_isfighter (lua_State* L)
1946{
1947 const bool result = AIL_ent->chr.teamDef->weapons || AIL_ent->chr.teamDef->onlyWeapon;
1948 lua_pushboolean(L, result);
1949 return 1;
1950}
1951
1955static int AIL_setwaypoint (lua_State* L)
1956{
1957 /* No waypoint, reset the count value to restart the search */
1958 if (lua_gettop(L) < 1 || lua_isnil(L, 1)) {
1959 AIL_ent->count = 100;
1960 lua_pushboolean(L, 1);
1961 } else if (lua_ispos3(L, 1)){
1962 pos3_t pos;
1964 Edict* waypoint = G_GetEdictFromPos(pos, ET_CIVILIANTARGET);
1965 if (waypoint != nullptr) {
1966 AIL_ent->count = waypoint->count;
1967 lua_pushboolean(L, 1);
1968 } else
1969 lua_pushboolean(L, 0);
1970 } else
1971 lua_pushboolean(L, 0);
1972
1973 return 1;
1974}
1975
1979static int AIL_difficulty (lua_State* L)
1980{
1981 lua_pushnumber(L, g_difficulty->value);
1982 return 1;
1983}
1984
1988static int AIL_positionflee (lua_State* L)
1989{
1990 int tus = AIL_ent->getUsableTUs();
1991 if (lua_gettop(L)) {
1992 if (lua_isnumber(L, 1))
1993 tus = std::min(static_cast<int>(lua_tonumber(L, 1)), tus);
1994 else
1996 }
1997
1998 /* Calculate move table. */
1999 G_MoveCalc(0, AIL_ent, AIL_ent->pos, AIL_ent->getUsableTUs());
2000 pos3_t oldPos;
2001 VectorCopy(AIL_ent->pos, oldPos);
2002
2003 const int radius = (tus + 1) / TU_MOVE_STRAIGHT;
2004 float bestScore = -1;
2005 pos3_t bestPos = {0, 0, PATHFINDING_HEIGHT};
2006 AiAreaSearch searchArea(AIL_ent->pos, radius);
2007 while (searchArea.getNext(AIL_ent->pos)) {
2008 const pos_t move = G_ActorMoveLength(AIL_ent, level.pathingMap, AIL_ent->pos, false);
2009 if (move >= ROUTING_NOT_REACHABLE || move > tus)
2010 continue;
2011 if (!AI_CheckPosition(AIL_ent, AIL_ent->pos))
2012 continue;
2013 float minDistFoe = -1.0f, minDistFriend = -1.0f;
2014 Actor* check = nullptr;
2015 while ((check = G_EdictsGetNextLivingActor(check))) {
2016 const float dist = VectorDist(AIL_ent->origin, check->origin);
2017 if (check->isSameTeamAs(AIL_ent)) {
2018 if (dist < minDistFriend || minDistFriend < 0.0f)
2019 minDistFriend = dist;
2020 } else if (AI_IsHostile(AIL_ent, check) || AIL_ent->isPanicked()) {
2021 if (dist < minDistFoe || minDistFoe < 0.0f)
2022 minDistFoe = dist;
2023 }
2024 }
2025 float score = minDistFoe - (minDistFriend / GRID_WIDTH);
2026 /* Try to hide */
2027 AIL_ent->calcOrigin();
2029 score /= UNIT_SIZE;
2030 if (score > bestScore) {
2031 bestScore = score;
2032 VectorCopy(AIL_ent->pos, bestPos);
2033 }
2034 }
2035 AIL_ent->setOrigin(oldPos);
2036
2037 if (bestPos[2] == PATHFINDING_HEIGHT) {
2038 lua_pushboolean(L, 0);
2039 } else {
2040 lua_pushpos3(L, &bestPos);
2041 }
2042
2043 return 1;
2044}
2045
2049static int AIL_weapontype (lua_State* L)
2050{
2051 const Item* right = AIL_ent->getRightHandItem();
2052 const Item* left = AIL_ent->getLeftHandItem();
2053
2054 lua_pushstring(L, right ? right->def()->type : "none");
2055 lua_pushstring(L, left ? left->def()->type : "none");
2056
2057 return 2;
2058}
2059
2063static int AIL_actor (lua_State* L)
2064{
2065 aiActor_t actor = {AIL_ent};
2066 lua_pushactor(L, &actor);
2067 return 1;
2068}
2069
2072static int AIL_tusforshooting (lua_State* L)
2073{
2074 int bestTUs = 256;
2075 const Item* weapon = AIL_ent->getRightHandItem();
2076 if (weapon) {
2077 const fireDef_t* fd = weapon->getFastestFireDef();
2078 if (fd)
2079 bestTUs = fd->time;
2080 }
2081 weapon = AIL_ent->getLeftHandItem();
2082 if (weapon) {
2083 const fireDef_t* fd = weapon->getFastestFireDef();
2084 if (fd) {
2085 const int tus = weapon->getFastestFireDef()->time;
2086 if (tus < bestTUs)
2087 bestTUs = tus;
2088 }
2089 }
2090
2091 lua_pushnumber(L, bestTUs);
2092 return 1;
2093}
2094
2098static int AIL_class (lua_State* L)
2099{
2100 lua_pushstring(L, AIL_ent->AI.subtype);
2101 return 1;
2102}
2103
2107static int AIL_hideneeded (lua_State* L)
2108{
2109 lua_pushboolean(L, AI_HideNeeded(AIL_ent));
2110 return 1;
2111}
2112
2118void AIL_ActorThink (Player& player, Actor* actor)
2119{
2120 /* Set the global player and edict */
2121 AIL_ent = actor;
2122 AIL_player = &player;
2123
2124 /* Try to run the function. */
2125 lua_getglobal(ailState, actor->AI.type);
2126 if (lua_istable(ailState, -1)) {
2127 lua_getfield(ailState, -1, "think");
2128 if (lua_pcall(ailState, 0, 0, 0)) { /* error has occured */
2129 gi.DPrintf("Error while running Lua: %s\n",
2130 lua_isstring(ailState, -1) ? lua_tostring(ailState, -1) : "Unknown Error");
2131 }
2132 } else {
2133 gi.DPrintf("Error while running Lua: AI for %s not found!\n", actor->AI.type);
2134 }
2135
2136 /* Cleanup */
2137 AIL_ent = nullptr;
2138 AIL_player = nullptr;
2139}
2140
2144static const char* AIL_GetAIType (const int team)
2145{
2146 const char* type;
2147 switch (team) {
2148 case TEAM_ALIEN:
2149 type = "alien";
2150 break;
2151 case TEAM_CIVILIAN:
2152 type = "civilian";
2153 break;
2154 case TEAM_PHALANX:
2155 default: /* Default to "soldier" AI for multiplayer teams */
2156 type = "soldier";
2157 break;
2158 }
2159 return type;
2160}
2161
2166bool AIL_TeamThink (Player& player)
2167{
2168 /* Set the global player */
2169 AIL_player = &player;
2170 AIL_ent = nullptr;
2171
2172 bool thinkAgain = false;
2173 /* Try to run the function. */
2174 lua_getglobal(ailState, AIL_GetAIType(player.getTeam()));
2175 if (lua_istable(ailState, -1)) {
2176 lua_getfield(ailState, -1, "team_think");
2177 if (lua_pcall(ailState, 0, 1, 0)) { /* error has occured */
2178 gi.DPrintf("Error while running Lua: %s\n",
2179 lua_isstring(ailState, -1) ? lua_tostring(ailState, -1) : "Unknown Error");
2180 } else
2181 thinkAgain = lua_toboolean(ailState, -1);
2182 } else {
2183 gi.DPrintf("Error while running Lua: AI for %s not found!\n", AIL_toTeamString(player.getTeam()));
2184 }
2185
2186 /* Cleanup */
2187 AIL_player = nullptr;
2188 AIL_ent = nullptr;
2189 return thinkAgain;
2190}
2191
2195static lua_State* AIL_InitLua () {
2196 /* Create the Lua state */
2197 lua_State* newState = luaL_newstate();
2198
2199 /* Register metatables. */
2200 actorL_register(newState);
2201 pos3L_register(newState);
2202
2203 /* Register libraries. */
2204 lua_newtable(newState);
2205 lua_pushvalue(newState, -1);
2206 lua_setglobal(newState, AI_METATABLE);
2207 luaL_setfuncs(newState, AIL_methods, 0);
2208
2209 return newState;
2210}
2211
2217{
2218 /* Prepare the AI */
2219 AI_t* AI = &actor->AI;
2220 Q_strncpyz(AI->type, AIL_GetAIType(actor->getTeam()), sizeof(AI->type));
2221 Q_strncpyz(AI->subtype, actor->chr.teamDef->id, sizeof(AI->subtype));
2222
2223 /* Create the a new Lua state if needed */
2224 if (ailState == nullptr)
2226
2227 if (ailState == nullptr) {
2228 gi.DPrintf("Unable to create Lua state.\n");
2229 return -1;
2230 }
2231
2232 /* Load the AI if needed */
2233 lua_getglobal(ailState, AI->type);
2234 if (!lua_istable(ailState, -1)) {
2235 char path[MAX_VAR];
2236 Com_sprintf(path, sizeof(path), "ai/%s.lua", AI->type);
2237 char* fbuf;
2238 const int size = gi.FS_LoadFile(path, (byte**) &fbuf);
2239 if (size == 0) {
2240 gi.DPrintf("Unable to load Lua file '%s'.\n", path);
2241 return -1;
2242 }
2243 if (luaL_dobuffer(ailState, fbuf, size, path)) {
2244 gi.DPrintf("Unable to parse Lua file '%s'\n", path);
2245 gi.DPrintf("%s\n", lua_isstring(ailState, -1) ? lua_tostring(ailState, -1) : "Unknown Error");
2246 gi.FS_FreeFile(fbuf);
2247 return -1;
2248 }
2249 lua_setglobal(ailState, AI->type);
2250 gi.FS_FreeFile(fbuf);
2251 } else {
2252 lua_pop(ailState, 1);
2253 }
2254
2255 return 0;
2256}
2257
2258void AIL_Init (void)
2259{
2260 gi.RegisterConstInt("luaaiteam::phalanx", TEAM_PHALANX);
2261 gi.RegisterConstInt("luaaiteam::civilian", TEAM_CIVILIAN);
2262 gi.RegisterConstInt("luaaiteam::alien", TEAM_ALIEN);
2263 gi.RegisterConstInt("luaaiteam::all", TEAM_ALL);
2264
2265 gi.RegisterConstInt("luaaivis::all", AILVT_ALL);
2266 gi.RegisterConstInt("luaaivis::sight", AILVT_SIGHT);
2267 gi.RegisterConstInt("luaaivis::team", AILVT_TEAM);
2268 gi.RegisterConstInt("luaaivis::extra", AILVT_DIST);
2269
2270 gi.RegisterConstInt("luaaisort::dist", AILSC_DIST);
2271 gi.RegisterConstInt("luaaisort::path", AILSC_PATH);
2272 gi.RegisterConstInt("luaaisort::HP", AILSC_HP);
2273
2274 gi.RegisterConstInt("luaaidist::dist", AILSC_DIST);
2275 gi.RegisterConstInt("luaaidist::path", AILSC_PATH);
2276
2277 gi.RegisterConstInt("luaaishot::fastest", AILSP_FAST);
2278 gi.RegisterConstInt("luaaishot::nearest", AILSP_NEAR);
2279 gi.RegisterConstInt("luaaishot::farthest", AILSP_FAR);
2280 gi.RegisterConstInt("luaaishot::best_dam", AILSP_DMG);
2281
2282 gi.RegisterConstInt("luaaiwander::rand", AILPW_RAND);
2283 gi.RegisterConstInt("luaaiwander::CW", AILPW_CW);
2284 gi.RegisterConstInt("luaaiwander::CCW", AILPW_CCW);
2285
2286 ailState = nullptr;
2287}
2288
2289void AIL_Shutdown (void)
2290{
2291 gi.UnregisterConstVariable("luaaiteam::phalanx");
2292 gi.UnregisterConstVariable("luaaiteam::civilian");
2293 gi.UnregisterConstVariable("luaaiteam::alien");
2294 gi.UnregisterConstVariable("luaaiteam::all");
2295
2296 gi.UnregisterConstVariable("luaaivis::all");
2297 gi.UnregisterConstVariable("luaaivis::sight");
2298 gi.UnregisterConstVariable("luaaivis::team");
2299 gi.UnregisterConstVariable("luaaivis::extra");
2300
2301 gi.UnregisterConstVariable("luaaisort::dist");
2302 gi.UnregisterConstVariable("luaaisort::path");
2303 gi.UnregisterConstVariable("luaaisort::HP");
2304
2305 gi.UnregisterConstVariable("luaaidist::dist");
2306 gi.UnregisterConstVariable("luaaidist::path");
2307
2308 gi.UnregisterConstVariable("luaaishot::fastest");
2309 gi.UnregisterConstVariable("luaaishot::nearest");
2310 gi.UnregisterConstVariable("luaaishot::farthest");
2311 gi.UnregisterConstVariable("luaaishot::best_dam");
2312
2313 gi.UnregisterConstVariable("luaaiwander::rand");
2314 gi.UnregisterConstVariable("luaaiwander::CW");
2315 gi.UnregisterConstVariable("luaaiwander::CCW");
2316}
2317
2321void AIL_Cleanup (void)
2322{
2323 lua_close(ailState);
2324 ailState = nullptr;
2325}
#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
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
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
AI_t AI
Definition g_edict.h:171
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
int count
Definition g_edict.h:135
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
const objDef_t * def(void) const
Definition inv_shared.h:469
const fireDef_t * getFiredefs() const
Returns the firedefinitions for a given weapon/ammo.
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.
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:211
static int AIL_toTeamInt(const char *team, const int param)
Converts team string into int representation.
Definition g_ai_lua.cpp:125
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:175
bool operator<(AilSortTable< T > i, AilSortTable< T > j)
Definition g_ai_lua.cpp:239
static int AIL_positionhide(lua_State *L)
Moves the actor into a position in which he can hide.
static int AIL_setwaypoint(lua_State *L)
Mark the current waypoint for a civ.
static int actorL_isinjured(lua_State *L)
Checks to see if the actor is seriously injured.
Definition g_ai_lua.cpp:771
static int AIL_reload(lua_State *L)
Actor reloads his weapons.
static lua_State * AIL_InitLua()
ailSortCritType_t
target sorting criteria (lowest first)
Definition g_ai_lua.cpp:81
@ AILSC_DIST
Definition g_ai_lua.cpp:82
@ AILSC_HP
Definition g_ai_lua.cpp:84
@ AILSC_PATH
Definition g_ai_lua.cpp:83
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:157
static int AIL_reactionfire(lua_State *L)
Sets the actor's reaction fire mode.
static int actorL_pos(lua_State *L)
Gets the actors position.
Definition g_ai_lua.cpp:521
static int AIL_roundsleft(lua_State *L)
Checks to see how many rounds the actor has left.
#define luaL_dobuffer(L, b, n, s)
Definition g_ai_lua.cpp:67
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...
#define AIL_invalidparameter(n)
Definition g_ai_lua.cpp:69
static int actorL_morale(lua_State *L)
Gets the current morale of the actor onto the stack.
Definition g_ai_lua.cpp:743
static int AIL_missiontargets(lua_State *L)
Returns the positions of the available mission targets.
static int AIL_grabweapon(lua_State *L)
Actor tries to grab a weapon from inventory.
static int AIL_actor(lua_State *L)
Returns the currently moving AI actor.
static int actorL_HP(lua_State *L)
Gets the number of HP the actor has left.
Definition g_ai_lua.cpp:725
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:871
ailVisType_t
vis check types
Definition g_ai_lua.cpp:73
@ AILVT_SIGHT
Definition g_ai_lua.cpp:75
@ AILVT_DIST
Definition g_ai_lua.cpp:77
@ AILVT_ALL
Definition g_ai_lua.cpp:74
@ AILVT_TEAM
Definition g_ai_lua.cpp:76
static const luaL_Reg AIL_methods[]
Definition g_ai_lua.cpp:398
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:139
int AIL_InitActor(Actor *actor)
Initializes the lua AI for an actor.
static Actor * AIL_ent
Definition g_ai_lua.cpp:246
static int actorL_isdead(lua_State *L)
Check if the actor is dead.
Definition g_ai_lua.cpp:809
static int AIL_positionapproach(lua_State *L)
Approach to a target actor.
ailWanderPosType
Definition g_ai_lua.cpp:95
@ AILPW_CCW
Definition g_ai_lua.cpp:98
@ AILPW_RAND
Definition g_ai_lua.cpp:96
@ AILPW_CW
Definition g_ai_lua.cpp:97
#define ACTOR_METATABLE
Definition g_ai_lua.cpp:60
static int actorL_isvalidtarget(lua_State *L)
Check if the actor is a valid target to attack.
Definition g_ai_lua.cpp:827
#define POS3_METATABLE
Definition g_ai_lua.cpp:59
static int pos3L_goto(lua_State *L)
Makes the actor head to the position.
Definition g_ai_lua.cpp:928
static int AIL_hideneeded(lua_State *L)
Check if the actor needs wants to hide.
static int AIL_difficulty(lua_State *L)
Return the difficulty number (in case we want different AI for different ones).
static const char * AIL_toTeamString(const int team)
Converts integer team representation into string.
Definition g_ai_lua.cpp:110
void AIL_Init(void)
static const luaL_Reg actorL_methods[]
Definition g_ai_lua.cpp:321
static const luaL_Reg pos3L_methods[]
Definition g_ai_lua.cpp:354
static int AIL_findweapons(lua_State *L)
Returns a table of the positions of nearby usable weapons on the floor.
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:464
static int AIL_positionshoot(lua_State *L)
Moves the actor into a position in which he can shoot his target.
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:900
static int actorL_shoot(lua_State *L)
Shoots the actor.
Definition g_ai_lua.cpp:533
static int actorL_register(lua_State *L)
Registers the actor metatable in the lua_State.
Definition g_ai_lua.cpp:440
static int AIL_weapontype(lua_State *L)
Returns the type of the weapons in the actors hands.
void AIL_Cleanup(void)
Closes the LUA AI.
static int pos3L_register(lua_State *L)
Registers the pos3 metatable in the lua_State.
Definition g_ai_lua.cpp:847
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:193
static int AIL_select(lua_State *L)
Select an specific AI actor.
void AIL_Shutdown(void)
static int AIL_tusforshooting(lua_State *L)
Returns the min TUs the actor needs to fire.
static int AIL_print(lua_State *L)
Works more or less like Lua's builtin print.
static int AIL_positionflee(lua_State *L)
Return a position to flee to.
static int actorL_team(lua_State *L)
Gets the actor's team.
Definition g_ai_lua.cpp:576
static int AIL_crouch(lua_State *L)
Requests a crouch state (with true/false) and returns current crouch state.
static const char * AIL_GetAIType(const int team)
Return the AI type for the given team (the lua file the team actors should run).
static int AIL_see(lua_State *L)
Returns what the actor can see.
bool AIL_TeamThink(Player &player)
The team think function for the ai controlled players.
static int actorL_throwgrenade(lua_State *L)
Throws a grenade to the actor.
Definition g_ai_lua.cpp:590
static int AIL_waypoints(lua_State *L)
Return the positions of the next waypoints.
#define AI_METATABLE
Definition g_ai_lua.cpp:61
static int pos3L_distance(lua_State *L)
Return the distance the position an the AI actor.
Definition g_ai_lua.cpp:971
static int pos3L_face(lua_State *L)
Makes the actor face the position.
Definition g_ai_lua.cpp:957
static int AIL_squad(lua_State *L)
Returns a table with the actors in the current player's team.
static int actorL_tostring(lua_State *L)
Pushes the actor as a string.
Definition g_ai_lua.cpp:505
static pos3_t * lua_topos3(lua_State *L, int index)
Returns the pos3 from the metatable at index.
Definition g_ai_lua.cpp:888
static int actorL_TU(lua_State *L)
Gets the number of usable TU the actor has left.
Definition g_ai_lua.cpp:707
static int AIL_positionwander(lua_State *L)
Return a new position to move to.
static Player * AIL_player
Definition g_ai_lua.cpp:247
static int pos3L_tostring(lua_State *L)
Puts the pos3 information in a string.
Definition g_ai_lua.cpp:912
static aiActor_t * lua_toactor(lua_State *L, int index)
Returns the actor from the metatable at index.
Definition g_ai_lua.cpp:481
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:249
static int AIL_canreload(lua_State *L)
Checks to see if the actor can reload.
ailShootPosType_t
Shooting position types.
Definition g_ai_lua.cpp:88
@ AILSP_FAST
Definition g_ai_lua.cpp:89
@ AILSP_DMG
Definition g_ai_lua.cpp:92
@ AILSP_FAR
Definition g_ai_lua.cpp:91
@ AILSP_NEAR
Definition g_ai_lua.cpp:90
static int AIL_positionmission(lua_State *L)
Try to find a position nearby to the given position.
void AIL_ActorThink(Player &player, Actor *actor)
The think function for the ai controlled players.
static int actorL_isarmed(lua_State *L)
Check if actor has weapons.
Definition g_ai_lua.cpp:790
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:493
static lua_State * ailState
Definition g_ai_lua.cpp:63
static int AIL_isfighter(lua_State *L)
Whether the current AI actor is a fighter or not.
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.
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:235
Wrapper around edict.
Definition g_ai_lua.cpp:226
Actor * actor
Definition g_ai_lua.cpp:227
const teamDef_t * teamDef
Definition chr_shared.h:413
char name[MAX_VAR]
Definition chr_shared.h:390
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
inventory definition for our menus
Definition inv_shared.h:371
const char * type
Definition inv_shared.h:271
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