File: | game/g_ai_lua.cpp |
Location: | line 366, column 32 |
Description: | Access to field 'ent' results in a dereference of a null pointer (loaded from variable 'target') |
1 | /** |
2 | * @file |
3 | * @brief Artificial Intelligence. |
4 | * |
5 | * @par |
6 | * You can find the reference lua manual at http://www.lua.org/manual/5.1/ |
7 | * |
8 | * @par -1 and -2 are pseudo indexes, they count backwards: |
9 | * @li -1 is top |
10 | * @li 1 is bottom |
11 | * @li -2 is under the top |
12 | * @li etc... |
13 | */ |
14 | |
15 | /* |
16 | Copyright (C) 2002-2011 UFO: Alien Invasion. |
17 | |
18 | This program is free software; you can redistribute it and/or |
19 | modify it under the terms of the GNU General Public License |
20 | as published by the Free Software Foundation; either version 2 |
21 | of the License, or (at your option) any later version. |
22 | |
23 | This program is distributed in the hope that it will be useful, |
24 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
25 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
26 | |
27 | See the GNU General Public License for more details. |
28 | |
29 | You should have received a copy of the GNU General Public License |
30 | along with this program; if not, write to the Free Software |
31 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
32 | |
33 | */ |
34 | |
35 | #include "g_local.h" |
36 | #include "g_ai.h" |
37 | #include "lua/lauxlib.h" |
38 | |
39 | #define POS3_METATABLE"pos3" "pos3" /**< Pos3 Lua Metatable name. */ |
40 | #define ACTOR_METATABLE"actor" "actor" /**< Actor Lua Metable name. */ |
41 | #define AI_METATABLE"ai" "ai" /**< AI Lua Metable name. */ |
42 | |
43 | /** |
44 | * Provides an api like luaL_dostring for buffers. |
45 | */ |
46 | #define luaL_dobuffer(L, b, n, s)(luaL_loadbuffer(L, b, n, s) || lua_pcall(L, 0, (-1), 0)) \ |
47 | (luaL_loadbuffer(L, b, n, s) || lua_pcall(L, 0, LUA_MULTRET(-1), 0)) |
48 | #define AIL_invalidparameter(n)gi.DPrintf("AIL: Invalid parameter #%d in '%s'.\n", n, __func__ ) \ |
49 | gi.DPrintf("AIL: Invalid parameter #%d in '%s'.\n", n, __func__) |
50 | |
51 | /* |
52 | * Helper functions |
53 | */ |
54 | |
55 | /** |
56 | * @brief Converts integer team representation into string |
57 | * @param team The team to convert to the string representation |
58 | * @return The team string |
59 | * @sa AIL_Init |
60 | */ |
61 | static const char *AIL_toTeamString (const int team) |
62 | { |
63 | const char *teamStr = gi.GetConstVariable("luaaiteam", team); |
64 | if (teamStr == NULL__null) |
65 | AIL_invalidparameter(1)gi.DPrintf("AIL: Invalid parameter #%d in '%s'.\n", 1, __func__ ); |
66 | return teamStr; |
67 | } |
68 | |
69 | /** |
70 | * @brief Converts team string into int representation |
71 | * @param team The team to convert (alien, phalanx, civilian, ...) |
72 | * @return The integer representation of the given team string |
73 | * @sa AIL_Init |
74 | */ |
75 | static int AIL_toTeamInt (const char *team) |
76 | { |
77 | int teamInt = TEAM_DEFAULT1; |
78 | if (!gi.GetConstIntFromNamespace("luaaiteam", team, &teamInt)) |
79 | AIL_invalidparameter(1)gi.DPrintf("AIL: Invalid parameter #%d in '%s'.\n", 1, __func__ ); |
80 | return teamInt; |
81 | } |
82 | |
83 | /** |
84 | * @brief Wrapper around edict. |
85 | */ |
86 | typedef struct aiActor_s { |
87 | edict_t *ent; /**< Actual actor. */ |
88 | } aiActor_t; |
89 | |
90 | |
91 | /* |
92 | * Current AI Actor. |
93 | */ |
94 | static edict_t *AIL_ent; /**< Actor currently running the Lua AI. */ |
95 | static player_t *AIL_player; /**< Player currently running the Lua AI. */ |
96 | |
97 | |
98 | /* |
99 | * Actor metatable. |
100 | */ |
101 | /* Internal functions. */ |
102 | static int actorL_register(lua_State *L); |
103 | static int lua_isactor(lua_State *L, int index); |
104 | static aiActor_t* lua_toactor(lua_State *L, int index); |
105 | static aiActor_t* lua_pushactor(lua_State *L, aiActor_t *actor); |
106 | /* Metatable functions. */ |
107 | static int actorL_tostring(lua_State *L); |
108 | static int actorL_pos(lua_State *L); |
109 | static int actorL_shoot(lua_State *L); |
110 | static int actorL_face(lua_State *L); |
111 | static int actorL_team(lua_State *L); |
112 | /** Lua Actor metatable methods. |
113 | * http://www.lua.org/manual/5.1/manual.html#lua_CFunction |
114 | */ |
115 | static const luaL_regluaL_Reg actorL_methods[] = { |
116 | {"__tostring", actorL_tostring}, |
117 | {"pos", actorL_pos}, |
118 | {"shoot", actorL_shoot}, |
119 | {"face", actorL_face}, |
120 | {"team", actorL_team}, |
121 | {NULL__null, NULL__null} |
122 | }; |
123 | |
124 | |
125 | /** |
126 | * pos3 metatable. |
127 | */ |
128 | /* Internal functions. */ |
129 | static int pos3L_register(lua_State *L); |
130 | static int lua_ispos3(lua_State *L, int index); |
131 | static pos3_t* lua_topos3(lua_State *L, int index); |
132 | static pos3_t* lua_pushpos3(lua_State *L, pos3_t *pos); |
133 | /* Metatable functions. */ |
134 | static int pos3L_tostring(lua_State *L); |
135 | static int pos3L_goto(lua_State *L); |
136 | static int pos3L_face(lua_State *L); |
137 | /** Lua Pos3 metatable methods. |
138 | * http://www.lua.org/manual/5.1/manual.html#lua_CFunction |
139 | */ |
140 | static const luaL_regluaL_Reg pos3L_methods[] = { |
141 | {"__tostring", pos3L_tostring}, |
142 | {"goto", pos3L_goto}, |
143 | {"face", pos3L_face}, |
144 | {NULL__null, NULL__null} |
145 | }; |
146 | |
147 | |
148 | /** |
149 | * General AI bindings. |
150 | */ |
151 | static int AIL_print(lua_State *L); |
152 | static int AIL_see(lua_State *L); |
153 | static int AIL_crouch(lua_State *L); |
154 | static int AIL_isinjured(lua_State *L); |
155 | static int AIL_TU(lua_State *L); |
156 | static int AIL_HP(lua_State *L); |
157 | static int AIL_morale(lua_State *L); |
158 | static int AIL_reactionfire(lua_State *L); |
159 | static int AIL_roundsleft(lua_State *L); |
160 | static int AIL_canreload(lua_State *L); |
161 | static int AIL_reload(lua_State *L); |
162 | static int AIL_positionshoot(lua_State *L); |
163 | static int AIL_positionhide(lua_State *L); |
164 | static int AIL_positionherd(lua_State *L); |
165 | static int AIL_distance(lua_State *L); |
166 | /** Lua AI module methods. |
167 | * http://www.lua.org/manual/5.1/manual.html#lua_CFunction |
168 | */ |
169 | static const luaL_regluaL_Reg AIL_methods[] = { |
170 | {"print", AIL_print}, |
171 | {"see", AIL_see}, |
172 | {"crouch", AIL_crouch}, |
173 | {"isinjured", AIL_isinjured}, |
174 | {"TU", AIL_TU}, |
175 | {"HP", AIL_HP}, |
176 | {"morale", AIL_morale}, |
177 | {"reactionfire", AIL_reactionfire}, |
178 | {"roundsleft", AIL_roundsleft}, |
179 | {"canreload", AIL_canreload}, |
180 | {"reload", AIL_reload}, |
181 | {"positionshoot", AIL_positionshoot}, |
182 | {"positionhide", AIL_positionhide}, |
183 | {"positionherd", AIL_positionherd}, |
184 | {"distance", AIL_distance}, |
185 | {NULL__null, NULL__null} |
186 | }; |
187 | |
188 | |
189 | /** |
190 | * A C T O R L |
191 | */ |
192 | |
193 | /** |
194 | * @brief Registers the actor metatable in the lua_State. |
195 | * @param[in,out] L State to register the metatable in. |
196 | * @return 0 on success. |
197 | */ |
198 | static int actorL_register (lua_State *L) |
199 | { |
200 | /* Create the metatable */ |
201 | luaL_newmetatable(L, ACTOR_METATABLE"actor"); |
202 | |
203 | /* Create the access table */ |
204 | lua_pushvalue(L, -1); |
205 | lua_setfield(L, -2, "__index"); |
206 | |
207 | /* Register the values */ |
208 | luaL_register(L, NULL__null, actorL_methods); |
209 | |
210 | /* Clean up stack. */ |
211 | lua_pop(L, 1)lua_settop(L, -(1)-1); |
212 | |
213 | return 0; /* No error */ |
214 | } |
215 | |
216 | /** |
217 | * @brief Checks to see if there is a actor metatable at index in the lua_State. |
218 | * @param[in,out] L Lua state to check. |
219 | * @param[in] index Index to check for a actor metatable. |
220 | * @return 1 if index has a actor metatable otherwise returns 0. |
221 | */ |
222 | static int lua_isactor (lua_State *L, int index) |
223 | { |
224 | int ret; |
225 | |
226 | if (lua_getmetatable(L, index) == 0) |
227 | return 0; |
228 | lua_getfield(L, LUA_REGISTRYINDEX(-10000), ACTOR_METATABLE"actor"); |
229 | |
230 | ret = 0; |
231 | if (lua_rawequal(L, -1, -2)) /* does it have the correct metatable? */ |
232 | ret = 1; |
233 | |
234 | lua_pop(L, 2)lua_settop(L, -(2)-1); /* remove both metatables */ |
235 | return ret; |
236 | } |
237 | |
238 | /** |
239 | * @brief Returns the actor from the metatable at index. |
240 | */ |
241 | static aiActor_t* lua_toactor (lua_State *L, int index) |
242 | { |
243 | if (lua_isactor(L, index)) { |
244 | return (aiActor_t*) lua_touserdata(L, index); |
245 | } |
246 | luaL_typerror(L, index, ACTOR_METATABLE"actor"); |
247 | return NULL__null; |
248 | } |
249 | |
250 | /** |
251 | * @brief Pushes a actor as a metatable at the top of the stack. |
252 | */ |
253 | static aiActor_t* lua_pushactor (lua_State *L, aiActor_t *actor) |
254 | { |
255 | aiActor_t *a; |
256 | a = (aiActor_t*) lua_newuserdata(L, sizeof(*a)); |
257 | *a = *actor; |
258 | luaL_getmetatable(L, ACTOR_METATABLE)(lua_getfield(L, (-10000), ("actor"))); |
259 | lua_setmetatable(L, -2); |
260 | return a; |
261 | } |
262 | |
263 | /** |
264 | * @brief Pushes the actor as a string. |
265 | */ |
266 | static int actorL_tostring (lua_State *L) |
267 | { |
268 | aiActor_t *target; |
269 | char buf[MAX_VAR64]; |
270 | |
271 | assert(lua_isactor(L, 1))(__builtin_expect(!(lua_isactor(L, 1)), 0) ? __assert_rtn(__func__ , "src/game/g_ai_lua.cpp", 271, "lua_isactor(L, 1)") : (void) 0); |
272 | |
273 | target = lua_toactor(L, 1); |
274 | Com_sprintf(buf, sizeof(buf), "Actor( %s )", target->ent->chr.name); |
275 | |
276 | lua_pushstring(L, buf); |
277 | return 1; |
278 | } |
279 | |
280 | /** |
281 | * @brief Gets the actors position. |
282 | */ |
283 | static int actorL_pos (lua_State *L) |
284 | { |
285 | aiActor_t *target; |
286 | |
287 | assert(lua_isactor(L, 1))(__builtin_expect(!(lua_isactor(L, 1)), 0) ? __assert_rtn(__func__ , "src/game/g_ai_lua.cpp", 287, "lua_isactor(L, 1)") : (void) 0); |
288 | |
289 | target = lua_toactor(L, 1); |
290 | lua_pushpos3(L, &target->ent->pos); |
291 | return 1; |
292 | } |
293 | |
294 | /** |
295 | * @brief Shoots the actor. |
296 | */ |
297 | static int actorL_shoot (lua_State *L) |
298 | { |
299 | int tu, shots; |
300 | shoot_types_t shootType; |
301 | aiActor_t *target; |
302 | const item_t *item; |
303 | const fireDef_t *fdArray; |
304 | |
305 | assert(lua_isactor(L, 1))(__builtin_expect(!(lua_isactor(L, 1)), 0) ? __assert_rtn(__func__ , "src/game/g_ai_lua.cpp", 305, "lua_isactor(L, 1)") : (void) 0); |
306 | |
307 | /* Target */ |
308 | target = lua_toactor(L, 1); |
309 | |
310 | /* Number of TU to spend shooting, fire mode will adjust to that. */ |
311 | if (lua_gettop(L) > 1) { |
312 | assert(lua_isnumber(L, 2))(__builtin_expect(!(lua_isnumber(L, 2)), 0) ? __assert_rtn(__func__ , "src/game/g_ai_lua.cpp", 312, "lua_isnumber(L, 2)") : (void )0); /* Must be a number. */ |
313 | |
314 | tu = (int) lua_tonumber(L, 2); |
315 | } else { |
316 | tu = AIL_ent->TU; |
317 | } |
318 | |
319 | shootType = ST_RIGHT0; |
320 | item = AI_GetItemForShootType(shootType, AIL_ent); |
321 | if (item == NULL__null) { |
322 | shootType = ST_LEFT2; |
323 | item = AI_GetItemForShootType(shootType, AIL_ent); |
324 | } |
325 | |
326 | /* Failure - no weapon. */ |
327 | if (item == NULL__null) { |
328 | lua_pushboolean(L, 0); |
329 | return 1; |
330 | } |
331 | |
332 | /** @todo Choose fire mode based on TU available - currently the first one is used. */ |
333 | fdArray = FIRESH_FiredefForWeapon(item); |
334 | if (fdArray == NULL__null) { |
335 | /* Failure - no weapon. */ |
336 | lua_pushboolean(L, 0); |
337 | return 1; |
338 | } |
339 | |
340 | shots = tu / fdArray->time; |
341 | |
342 | while (shots > 0) { |
343 | shots--; |
344 | /** @todo actually handle fire modes */ |
345 | G_ClientShoot(AIL_player, AIL_ent, target->ent->pos, |
346 | shootType, 0, NULL__null, true, 0); |
347 | } |
348 | |
349 | /* Success. */ |
350 | lua_pushboolean(L, 1); |
351 | return 1; |
352 | } |
353 | |
354 | /** |
355 | * @brief Makes the actor face the position. |
356 | */ |
357 | static int actorL_face (lua_State *L) |
358 | { |
359 | aiActor_t *target; |
360 | |
361 | assert(lua_isactor(L, 1))(__builtin_expect(!(lua_isactor(L, 1)), 0) ? __assert_rtn(__func__ , "src/game/g_ai_lua.cpp", 361, "lua_isactor(L, 1)") : (void) 0); |
362 | |
363 | /* Target */ |
364 | target = lua_toactor(L, 1); |
365 | |
366 | AI_TurnIntoDirection(AIL_ent, target->ent->pos); |
Access to field 'ent' results in a dereference of a null pointer (loaded from variable 'target') | |
367 | |
368 | /* Success. */ |
369 | lua_pushboolean(L, 1); |
370 | return 1; |
371 | } |
372 | |
373 | /** |
374 | * @brief Gets the actor's team. |
375 | */ |
376 | static int actorL_team (lua_State *L) |
377 | { |
378 | const aiActor_t *target; |
379 | const char *team; |
380 | |
381 | assert(lua_isactor(L, 1))(__builtin_expect(!(lua_isactor(L, 1)), 0) ? __assert_rtn(__func__ , "src/game/g_ai_lua.cpp", 381, "lua_isactor(L, 1)") : (void) 0); |
382 | |
383 | target = lua_toactor(L, 1); |
384 | assert(target != NULL)(__builtin_expect(!(target != __null), 0) ? __assert_rtn(__func__ , "src/game/g_ai_lua.cpp", 384, "target != NULL") : (void)0); |
385 | team = AIL_toTeamString(target->ent->team); |
386 | lua_pushstring(L, team); |
387 | return 1; |
388 | } |
389 | |
390 | |
391 | /** |
392 | * P O S 3 L |
393 | */ |
394 | |
395 | /** |
396 | * @brief Registers the pos3 metatable in the lua_State. |
397 | * @param[in] L State to register the metatable in. |
398 | * @return 0 on success. |
399 | */ |
400 | static int pos3L_register (lua_State *L) |
401 | { |
402 | /* Create the metatable */ |
403 | luaL_newmetatable(L, POS3_METATABLE"pos3"); |
404 | |
405 | /* Create the access table */ |
406 | lua_pushvalue(L, -1); |
407 | lua_setfield(L, -2, "__index"); |
408 | |
409 | /* Register the values */ |
410 | luaL_register(L, NULL__null, pos3L_methods); |
411 | |
412 | /* Clean up the stack. */ |
413 | lua_pop(L, 1)lua_settop(L, -(1)-1); |
414 | |
415 | return 0; /* No error */ |
416 | } |
417 | |
418 | /** |
419 | * @brief Checks to see if there is a pos3 metatable at index in the lua_State. |
420 | * @param[in] L Lua state to check. |
421 | * @param[in] index Index to check for a pos3 metatable. |
422 | * @return 1 if index has a pos3 metatable otherwise returns 0. |
423 | */ |
424 | static int lua_ispos3 (lua_State *L, int index) |
425 | { |
426 | int ret; |
427 | |
428 | if (lua_getmetatable(L, index) == 0) |
429 | return 0; |
430 | lua_getfield(L, LUA_REGISTRYINDEX(-10000), POS3_METATABLE"pos3"); |
431 | |
432 | ret = 0; |
433 | if (lua_rawequal(L, -1, -2)) /* does it have the correct metatable? */ |
434 | ret = 1; |
435 | |
436 | lua_pop(L, 2)lua_settop(L, -(2)-1); /* remove both metatables */ |
437 | return ret; |
438 | } |
439 | |
440 | /** |
441 | * @brief Returns the pos3 from the metatable at index. |
442 | */ |
443 | static pos3_t* lua_topos3 (lua_State *L, int index) |
444 | { |
445 | if (lua_ispos3(L, index)) { |
446 | return (pos3_t*) lua_touserdata(L, index); |
447 | } |
448 | luaL_typerror(L, index, POS3_METATABLE"pos3"); |
449 | return NULL__null; |
450 | } |
451 | |
452 | /** |
453 | * @brief Pushes a pos3 as a metatable at the top of the stack. |
454 | */ |
455 | static pos3_t* lua_pushpos3 (lua_State *L, pos3_t *pos) |
456 | { |
457 | pos3_t *p; |
458 | p = (pos3_t*) lua_newuserdata(L, sizeof(*p)); |
459 | memcpy(p, pos, sizeof(*p)); |
460 | luaL_getmetatable(L, POS3_METATABLE)(lua_getfield(L, (-10000), ("pos3"))); |
461 | lua_setmetatable(L, -2); |
462 | return p; |
463 | } |
464 | |
465 | /** |
466 | * @brief Puts the pos3 information in a string. |
467 | */ |
468 | static int pos3L_tostring (lua_State *L) |
469 | { |
470 | pos3_t *p; |
471 | char buf[MAX_VAR64]; |
472 | |
473 | assert(lua_ispos3(L, 1))(__builtin_expect(!(lua_ispos3(L, 1)), 0) ? __assert_rtn(__func__ , "src/game/g_ai_lua.cpp", 473, "lua_ispos3(L, 1)") : (void)0 ); |
474 | |
475 | p = lua_topos3(L, 1); |
476 | Com_sprintf(buf, sizeof(buf), "Pos3( x=%d, y=%d, z=%d )", (*p)[0], (*p)[1], (*p)[2]); |
477 | |
478 | lua_pushstring(L, buf); |
479 | return 1; |
480 | } |
481 | |
482 | /** |
483 | * @brief Makes the actor head to the position. |
484 | */ |
485 | static int pos3L_goto (lua_State *L) |
486 | { |
487 | pos3_t *pos; |
488 | const byte crouchingState = G_IsCrouched(AIL_ent)((AIL_ent)->state & (0x0004)) ? 1 : 0; |
489 | |
490 | assert(lua_ispos3(L, 1))(__builtin_expect(!(lua_ispos3(L, 1)), 0) ? __assert_rtn(__func__ , "src/game/g_ai_lua.cpp", 490, "lua_ispos3(L, 1)") : (void)0 ); |
491 | |
492 | /* Calculate move table. */ |
493 | G_MoveCalc(0, AIL_ent, AIL_ent->pos, crouchingState, AIL_ent->TU); |
494 | gi.MoveStore(level.pathingMap); |
495 | |
496 | /* Move. */ |
497 | pos = lua_topos3(L, 1); |
498 | G_ClientMove(AIL_player, 0, AIL_ent, *pos); |
499 | |
500 | lua_pushboolean(L, 1); |
501 | return 1; |
502 | } |
503 | |
504 | /** |
505 | * @brief Makes the actor face the position. |
506 | */ |
507 | static int pos3L_face (lua_State *L) |
508 | { |
509 | pos3_t *pos; |
510 | |
511 | assert(lua_ispos3(L, 1))(__builtin_expect(!(lua_ispos3(L, 1)), 0) ? __assert_rtn(__func__ , "src/game/g_ai_lua.cpp", 511, "lua_ispos3(L, 1)") : (void)0 ); |
512 | |
513 | pos = lua_topos3(L, 1); |
514 | AI_TurnIntoDirection(AIL_ent, *pos); |
515 | |
516 | lua_pushboolean(L, 1); |
517 | return 1; |
518 | } |
519 | |
520 | /** |
521 | * A I L |
522 | */ |
523 | /** |
524 | * @brief Works more or less like Lua's builtin print. |
525 | */ |
526 | static int AIL_print (lua_State *L) |
527 | { |
528 | int i; |
529 | const int n = lua_gettop(L); /* number of arguments */ |
530 | |
531 | for (i = 1; i <= n; i++) { |
532 | const char *s; |
533 | bool meta = false; |
534 | |
535 | lua_pushvalue(L, i); /* value to print */ |
536 | if (luaL_callmeta(L, 1, "__tostring")) { |
537 | s = lua_tostring(L, -1)lua_tolstring(L, (-1), __null); |
538 | meta = true; |
539 | } else { |
540 | switch (lua_type(L, -1)) { |
541 | case LUA_TNUMBER3: |
542 | case LUA_TSTRING4: |
543 | s = lua_tostring(L, -1)lua_tolstring(L, (-1), __null); |
544 | break; |
545 | case LUA_TBOOLEAN1: |
546 | s = lua_toboolean(L, -1) ? "true" : "false"; |
547 | break; |
548 | case LUA_TNIL0: |
549 | s = "nil"; |
550 | break; |
551 | |
552 | default: |
553 | s = "unknown lua type"; |
554 | break; |
555 | } |
556 | } |
557 | gi.DPrintf("%s%s", (i > 1) ? "\t" : "", s); |
558 | lua_pop(L, 1)lua_settop(L, -(1)-1); /* Pop the value */ |
559 | if (meta) /* Meta creates an additional string. */ |
560 | lua_pop(L, 1)lua_settop(L, -(1)-1); |
561 | } |
562 | |
563 | gi.DPrintf("\n"); |
564 | return 0; |
565 | } |
566 | |
567 | /** |
568 | * @brief Returns what the actor can see. |
569 | */ |
570 | static int AIL_see (lua_State *L) |
571 | { |
572 | int vision, team; |
573 | int i, j, k, n, cur; |
574 | edict_t *check = NULL__null; |
575 | aiActor_t target; |
576 | edict_t *sorted[MAX_EDICTS1024], *unsorted[MAX_EDICTS1024]; |
577 | float distLookup[MAX_EDICTS1024]; |
578 | |
579 | /* Defaults. */ |
580 | team = TEAM_ALL0xFFFFFFFF; |
581 | vision = 0; |
582 | |
583 | /* Handle parameters. */ |
584 | if ((lua_gettop(L) > 0)) { |
585 | /* Get what to "see" with. */ |
586 | if (lua_isstring(L, 1)) { |
587 | const char *s = lua_tostring(L, 1)lua_tolstring(L, (1), __null); |
588 | /** @todo Properly implement at edict level, get rid of magic numbers. |
589 | * These are only "placeholders". */ |
590 | if (Q_streq(s, "all")(strcmp(s, "all") == 0)) |
591 | vision = 0; |
592 | else if (Q_streq(s, "sight")(strcmp(s, "sight") == 0)) |
593 | vision = 1; |
594 | else if (Q_streq(s, "psionic")(strcmp(s, "psionic") == 0)) |
595 | vision = 2; |
596 | else if (Q_streq(s, "infrared")(strcmp(s, "infrared") == 0)) |
597 | vision = 3; |
598 | else |
599 | AIL_invalidparameter(1)gi.DPrintf("AIL: Invalid parameter #%d in '%s'.\n", 1, __func__ ); |
600 | } else |
601 | AIL_invalidparameter(1)gi.DPrintf("AIL: Invalid parameter #%d in '%s'.\n", 1, __func__ ); |
602 | |
603 | /* We now check for different teams. */ |
604 | if ((lua_gettop(L) > 1)) { |
605 | if (lua_isstring(L, 2)) { |
606 | const char *s = lua_tostring(L, 2)lua_tolstring(L, (2), __null); |
607 | team = AIL_toTeamInt(s); |
608 | } else |
609 | AIL_invalidparameter(2)gi.DPrintf("AIL: Invalid parameter #%d in '%s'.\n", 2, __func__ ); |
610 | } |
611 | } |
612 | |
613 | n = 0; |
614 | /* Get visible things. */ |
615 | while ((check = G_EdictsGetNextLivingActor(check))) { |
616 | if (AIL_ent == check) |
617 | continue; |
618 | if (vision == 0 && (team == TEAM_ALL0xFFFFFFFF || check->team == team) /* Check for team match if needed. */ |
619 | && G_Vis(AIL_ent->team, AIL_ent, check, VT_NOFRUSTUM2)) { |
620 | distLookup[n] = VectorDistSqr(AIL_ent->pos, check->pos)(((check->pos)[0]-(AIL_ent->pos)[0])*((check->pos)[0 ]-(AIL_ent->pos)[0])+((check->pos)[1]-(AIL_ent->pos) [1])*((check->pos)[1]-(AIL_ent->pos)[1])+((check->pos )[2]-(AIL_ent->pos)[2])*((check->pos)[2]-(AIL_ent->pos )[2])); |
621 | unsorted[n++] = check; |
622 | } |
623 | } |
624 | |
625 | /* Sort by distance - nearest first. */ |
626 | for (i = 0; i < n; i++) { /* Until we fill sorted */ |
627 | cur = -1; |
628 | for (j = 0; j < n; j++) { /* Check for closest */ |
629 | /* Is shorter then current minimum? */ |
630 | if (cur < 0 || distLookup[j] < distLookup[cur]) { |
631 | /* Check if not already in sorted. */ |
632 | for (k = 0; k < i; k++) |
633 | if (sorted[k] == unsorted[j]) |
634 | break; |
635 | |
636 | /* Not already sorted and is new minimum. */ |
637 | if (k == i) |
638 | cur = j; |
639 | } |
640 | } |
641 | |
642 | sorted[i] = unsorted[cur]; |
643 | } |
644 | |
645 | /* Now save it in a Lua table. */ |
646 | lua_newtable(L)lua_createtable(L, 0, 0); |
647 | for (i = 0; i < n; i++) { |
648 | lua_pushnumber(L, i + 1); /* index, starts with 1 */ |
649 | target.ent = sorted[i]; |
650 | lua_pushactor(L, &target); /* value */ |
651 | lua_rawset(L, -3); /* store the value in the table */ |
652 | } |
653 | return 1; /* Returns the table of actors. */ |
654 | } |
655 | |
656 | /** |
657 | * @brief Toggles crouch state with true/false and returns current crouch state. |
658 | */ |
659 | static int AIL_crouch (lua_State *L) |
660 | { |
661 | if (lua_gettop(L) > 0) { |
662 | if (lua_isboolean(L, 1)(lua_type(L, (1)) == 1)) { |
663 | const int state = lua_toboolean(L, 1); |
664 | G_ClientStateChange(AIL_player, AIL_ent, STATE_CROUCHED0x0004, |
665 | (state) ? true : false); |
666 | } else |
667 | AIL_invalidparameter(1)gi.DPrintf("AIL: Invalid parameter #%d in '%s'.\n", 1, __func__ ); |
668 | } |
669 | |
670 | lua_pushboolean(L, G_IsCrouched(AIL_ent)((AIL_ent)->state & (0x0004))); |
671 | return 1; |
672 | } |
673 | |
674 | /** |
675 | * @brief Checks to see if the actor is injured |
676 | */ |
677 | static int AIL_isinjured (lua_State *L) |
678 | { |
679 | lua_pushboolean(L, AIL_ent->HP != AIL_ent->chr.maxHP); |
680 | return 1; |
681 | } |
682 | |
683 | /** |
684 | * @brief Gets the number of TU the actor has left. |
685 | */ |
686 | static int AIL_TU (lua_State *L) |
687 | { |
688 | lua_pushnumber(L, AIL_ent->TU); |
689 | return 1; |
690 | } |
691 | |
692 | /** |
693 | * @brief Gets the number of HP the actor has left. |
694 | */ |
695 | static int AIL_HP (lua_State *L) |
696 | { |
697 | lua_pushnumber(L, AIL_ent->HP); |
698 | return 1; |
699 | } |
700 | |
701 | /** |
702 | * @brief Gets the current morale of the actor onto the stack. |
703 | */ |
704 | static int AIL_morale (lua_State *L) |
705 | { |
706 | lua_pushnumber(L, AIL_ent->morale); |
707 | return 1; |
708 | } |
709 | |
710 | /** |
711 | * @brief Sets the actor's reaction fire mode. |
712 | */ |
713 | static int AIL_reactionfire (lua_State *L) |
714 | { |
715 | int reactionState = 0; |
716 | if (lua_gettop(L) > 0) { |
717 | |
718 | if (lua_isstring(L, 1)) { |
719 | /* get reaction fire mode */ |
720 | const char* cmd = lua_tostring(L, 1)lua_tolstring(L, (1), __null); |
721 | reactionState = Q_streq(cmd, "disable")(strcmp(cmd, "disable") == 0) ? ~STATE_REACTION0x0300 : STATE_REACTION0x0300; |
722 | } |
723 | |
724 | if (reactionState && lua_gettop(L) > 1 && lua_isboolean(L, 2)(lua_type(L, (2)) == 1)) { |
725 | const int state = lua_toboolean(L, 2); |
726 | G_ClientStateChange(AIL_player, AIL_ent, reactionState, |
727 | (state) ? true : false); |
728 | } else { |
729 | AIL_invalidparameter(reactionState ? 2 : 1)gi.DPrintf("AIL: Invalid parameter #%d in '%s'.\n", reactionState ? 2 : 1, __func__); |
730 | } |
731 | } |
732 | |
733 | lua_pushboolean(L, G_IsReaction(AIL_ent)((AIL_ent)->state & (0x0300))); |
734 | return 1; |
735 | } |
736 | |
737 | /** |
738 | * @brief Checks to see how many rounds the actor has left. |
739 | */ |
740 | static int AIL_roundsleft (lua_State *L) |
741 | { |
742 | /* Right hand */ |
743 | if (RIGHT(AIL_ent)(((AIL_ent)->chr.i.c[(gi.csi->idRight)])) && RIGHT(AIL_ent)(((AIL_ent)->chr.i.c[(gi.csi->idRight)]))->item.t->reload) |
744 | lua_pushnumber(L, RIGHT(AIL_ent)(((AIL_ent)->chr.i.c[(gi.csi->idRight)]))->item.a); |
745 | else |
746 | lua_pushnil(L); |
747 | |
748 | /* Left hand */ |
749 | if (LEFT(AIL_ent)(((AIL_ent)->chr.i.c[(gi.csi->idLeft)])) && LEFT(AIL_ent)(((AIL_ent)->chr.i.c[(gi.csi->idLeft)]))->item.t->reload) |
750 | lua_pushnumber(L, LEFT(AIL_ent)(((AIL_ent)->chr.i.c[(gi.csi->idLeft)]))->item.a); |
751 | else |
752 | lua_pushnil(L); |
753 | return 2; |
754 | } |
755 | |
756 | /** |
757 | * @brief Checks to see if the actor can reload. |
758 | */ |
759 | static int AIL_canreload (lua_State *L) |
760 | { |
761 | lua_pushboolean(L, G_ClientCanReload(AIL_ent, gi.csi->idRight)); |
762 | lua_pushboolean(L, G_ClientCanReload(AIL_ent, gi.csi->idLeft)); |
763 | return 2; |
764 | } |
765 | |
766 | /** |
767 | * @brief Actor reloads his weapons. |
768 | */ |
769 | static int AIL_reload (lua_State *L) |
770 | { |
771 | containerIndex_t container; |
772 | |
773 | if (lua_gettop(L) > 0) { |
774 | if (lua_isstring(L, 1)) { |
775 | const char *s = lua_tostring(L, 1)lua_tolstring(L, (1), __null); |
776 | |
777 | if (Q_streq(s, "right")(strcmp(s, "right") == 0)) |
778 | container = gi.csi->idRight; |
779 | else if (Q_streq(s, "left")(strcmp(s, "left") == 0)) |
780 | container = gi.csi->idLeft; |
781 | else |
782 | return 0; |
783 | } else { |
784 | AIL_invalidparameter(1)gi.DPrintf("AIL: Invalid parameter #%d in '%s'.\n", 1, __func__ ); |
785 | return 0; |
786 | } |
787 | } else |
788 | container = gi.csi->idRight; /* Default to right hand. */ |
789 | |
790 | G_ActorReload(AIL_ent, INVDEF(container)(&gi.csi->ids[(container)])); |
791 | return 0; |
792 | } |
793 | |
794 | /** |
795 | * @brief Moves the actor into a position in which he can shoot his target. |
796 | */ |
797 | static int AIL_positionshoot (lua_State *L) |
798 | { |
799 | pos3_t to, bestPos; |
800 | vec3_t check; |
801 | edict_t *ent; |
802 | int dist; |
803 | int xl, yl, xh, yh; |
804 | int min_tu; |
805 | aiActor_t *target; |
806 | const byte crouchingState = G_IsCrouched(AIL_ent)((AIL_ent)->state & (0x0004)) ? 1 : 0; |
807 | |
808 | /* We need a target. */ |
809 | assert(lua_isactor(L, 1))(__builtin_expect(!(lua_isactor(L, 1)), 0) ? __assert_rtn(__func__ , "src/game/g_ai_lua.cpp", 809, "lua_isactor(L, 1)") : (void) 0); |
810 | target = lua_toactor(L, 1); |
811 | |
812 | /* Make things more simple. */ |
813 | ent = AIL_ent; |
814 | dist = ent->TU; |
815 | |
816 | /* Calculate move table. */ |
817 | G_MoveCalc(0, ent, ent->pos, crouchingState, ent->TU); |
818 | gi.MoveStore(level.pathingMap); |
819 | |
820 | /* set borders */ |
821 | xl = (int) ent->pos[0] - dist; |
822 | if (xl < 0) |
823 | xl = 0; |
824 | yl = (int) ent->pos[1] - dist; |
825 | if (yl < 0) |
826 | yl = 0; |
827 | xh = (int) ent->pos[0] + dist; |
828 | if (xh > PATHFINDING_WIDTH((4096 / 32) * 2)) |
829 | xl = PATHFINDING_WIDTH((4096 / 32) * 2); |
830 | yh = (int) ent->pos[1] + dist; |
831 | if (yh > PATHFINDING_WIDTH((4096 / 32) * 2)) |
832 | yh = PATHFINDING_WIDTH((4096 / 32) * 2); |
833 | |
834 | /* evaluate moving to every possible location in the search area, |
835 | * including combat considerations */ |
836 | min_tu = INT_MAX2147483647; |
837 | for (to[2] = 0; to[2] < PATHFINDING_HEIGHT8; to[2]++) |
838 | for (to[1] = yl; to[1] < yh; to[1]++) |
839 | for (to[0] = xl; to[0] < xh; to[0]++) { |
840 | pos_t tu; |
841 | /* Can we see the target? */ |
842 | gi.GridPosToVec(gi.routingMap, ent->fieldSize, to, check); |
843 | tu = gi.MoveLength(level.pathingMap, to, G_IsCrouched(ent)((ent)->state & (0x0004)) ? 1 : 0, true); |
844 | if (tu > ent->TU || tu == ROUTING_NOT_REACHABLE0xFF) |
845 | continue; |
846 | /* Better spot (easier to get to). */ |
847 | if (tu < min_tu) { |
848 | if (G_ActorVis(check, ent, target->ent, true) > 0.3) { |
849 | VectorCopy(to, bestPos)((bestPos)[0]=(to)[0],(bestPos)[1]=(to)[1],(bestPos)[2]=(to)[ 2]); |
850 | min_tu = tu; |
851 | } |
852 | } |
853 | } |
854 | |
855 | /* No position found in range. */ |
856 | if (min_tu > ent->TU) { |
857 | lua_pushboolean(L, 0); |
858 | return 1; |
859 | } |
860 | |
861 | /* Return the spot. */ |
862 | lua_pushpos3(L, &bestPos); |
863 | return 1; |
864 | } |
865 | |
866 | /** |
867 | * @brief Moves the actor into a position in which he can hide. |
868 | * @note @c team (parameter is passed through the lua stack) means that the AI tries to find |
869 | * a hide position from the @c team members, if parameter is empty - from any enemy |
870 | */ |
871 | static int AIL_positionhide (lua_State *L) |
872 | { |
873 | pos3_t save; |
874 | int tus = AIL_ent->TU; |
875 | int hidingTeam; |
876 | |
877 | VectorCopy(AIL_ent->pos, save)((save)[0]=(AIL_ent->pos)[0],(save)[1]=(AIL_ent->pos)[1 ],(save)[2]=(AIL_ent->pos)[2]); |
878 | |
879 | hidingTeam = AI_GetHidingTeam(AIL_ent); |
880 | |
881 | /* parse parameter */ |
882 | if (lua_gettop(L)) { |
883 | if (lua_isstring(L, 1)) { |
884 | const char* s = lua_tostring(L, 1)lua_tolstring(L, (1), __null); |
885 | hidingTeam = AIL_toTeamInt(s); |
886 | if (hidingTeam == TEAM_ALL0xFFFFFFFF) |
887 | AIL_invalidparameter(1)gi.DPrintf("AIL: Invalid parameter #%d in '%s'.\n", 1, __func__ ); |
888 | } else { |
889 | AIL_invalidparameter(1)gi.DPrintf("AIL: Invalid parameter #%d in '%s'.\n", 1, __func__ ); |
890 | } |
891 | } |
892 | |
893 | if (AI_FindHidingLocation(hidingTeam, AIL_ent, AIL_ent->pos, &tus)) { |
894 | /* Return the spot. */ |
895 | lua_pushpos3(L, &AIL_ent->pos); |
896 | } else { |
897 | lua_pushboolean(L, 0); |
898 | } |
899 | G_EdictSetOrigin(AIL_ent, save); |
900 | return 1; |
901 | } |
902 | |
903 | /** |
904 | * @brief Determine the position where actor is more closer to the target and |
905 | * locate behind the target from enemy |
906 | * @note @c target (parameter is passed through the lua stack) The actor |
907 | * to which AI tries to become closer |
908 | */ |
909 | static int AIL_positionherd (lua_State *L) |
910 | { |
911 | pos3_t save; |
912 | aiActor_t* target; |
913 | |
914 | /* check parameter */ |
915 | if (!(lua_gettop(L) && lua_isactor(L, 1))) { |
916 | AIL_invalidparameter(1)gi.DPrintf("AIL: Invalid parameter #%d in '%s'.\n", 1, __func__ ); |
917 | lua_pushboolean(L, 0); |
918 | return 1; |
919 | } |
920 | |
921 | VectorCopy(AIL_ent->pos, save)((save)[0]=(AIL_ent->pos)[0],(save)[1]=(AIL_ent->pos)[1 ],(save)[2]=(AIL_ent->pos)[2]); |
922 | target = lua_toactor(L, 1); |
923 | if (AI_FindHerdLocation(AIL_ent, AIL_ent->pos, target->ent->origin, AIL_ent->TU)) { |
924 | lua_pushpos3(L, &AIL_ent->pos); |
925 | } else { |
926 | lua_pushboolean(L, 0); |
927 | } |
928 | G_EdictSetOrigin(AIL_ent, save); |
929 | return 1; |
930 | } |
931 | |
932 | /** |
933 | * @brief Returns distance between AI and target |
934 | * @note @c target (passed trough the lua stack) The target to which the distance is calculated |
935 | */ |
936 | static int AIL_distance (lua_State *L) |
937 | { |
938 | vec_t dist; |
939 | aiActor_t* target; |
940 | |
941 | /* check parameter */ |
942 | assert(lua_gettop(L) && lua_isactor(L, 1))(__builtin_expect(!(lua_gettop(L) && lua_isactor(L, 1 )), 0) ? __assert_rtn(__func__, "src/game/g_ai_lua.cpp", 942, "lua_gettop(L) && lua_isactor(L, 1)") : (void)0); |
943 | |
944 | /* calculate distance */ |
945 | target = lua_toactor(L, 1); |
946 | dist = VectorDist(AIL_ent->origin, target->ent->origin)(sqrt(((target->ent->origin)[0]-(AIL_ent->origin)[0] )*((target->ent->origin)[0]-(AIL_ent->origin)[0])+(( target->ent->origin)[1]-(AIL_ent->origin)[1])*((target ->ent->origin)[1]-(AIL_ent->origin)[1])+((target-> ent->origin)[2]-(AIL_ent->origin)[2])*((target->ent-> origin)[2]-(AIL_ent->origin)[2]))); |
947 | lua_pushnumber(L, dist); |
948 | return 1; |
949 | } |
950 | |
951 | /** |
952 | * @brief The think function for the ai controlled aliens |
953 | * @param[in] player |
954 | * @param[in] ent |
955 | * @sa AI_FighterCalcBestAction |
956 | * @sa AI_CivilianCalcBestAction |
957 | * @sa G_ClientMove |
958 | * @sa G_ClientShoot |
959 | */ |
960 | void AIL_ActorThink (player_t * player, edict_t * ent) |
961 | { |
962 | lua_State *L; |
963 | |
964 | /* The Lua State we will work with. */ |
965 | L = ent->AI.L; |
966 | |
967 | /* Set the global player and edict */ |
968 | AIL_ent = ent; |
969 | AIL_player = player; |
970 | |
971 | /* Try to run the function. */ |
972 | lua_getglobal(L, "think")lua_getfield(L, (-10002), ("think")); |
973 | if (lua_pcall(L, 0, 0, 0)) { /* error has occured */ |
974 | gi.DPrintf("Error while running Lua: %s\n", |
975 | lua_isstring(L, -1) ? lua_tostring(L, -1)lua_tolstring(L, (-1), __null) : "Unknown Error"); |
976 | } |
977 | |
978 | /* Cleanup */ |
979 | AIL_ent = NULL__null; |
980 | AIL_player = NULL__null; |
981 | } |
982 | |
983 | |
984 | /** |
985 | * @brief Initializes the AI. |
986 | * @param[in] ent Pointer to actor to initialize AI for. |
987 | * @param[in] type Type of AI (Lua file name without .lua). |
988 | * @param[in] subtype Subtype of the AI. |
989 | * @return 0 on success. |
990 | */ |
991 | int AIL_InitActor (edict_t * ent, const char *type, const char *subtype) |
992 | { |
993 | AI_t *AI; |
994 | int size; |
995 | char path[MAX_VAR64]; |
996 | char *fbuf; |
997 | |
998 | /* Prepare the AI */ |
999 | AI = &ent->AI; |
1000 | Q_strncpyz(AI->type, type, sizeof(AI->type))Q_strncpyzDebug( AI->type, type, sizeof(AI->type), "src/game/g_ai_lua.cpp" , 1000 ); |
1001 | Q_strncpyz(AI->subtype, subtype, sizeof(AI->type))Q_strncpyzDebug( AI->subtype, subtype, sizeof(AI->type) , "src/game/g_ai_lua.cpp", 1001 ); |
1002 | |
1003 | /* Create the new Lua state */ |
1004 | AI->L = luaL_newstate(); |
1005 | if (AI->L == NULL__null) { |
1006 | gi.DPrintf("Unable to create Lua state.\n"); |
1007 | return -1; |
1008 | } |
1009 | |
1010 | /* Register metatables. */ |
1011 | actorL_register(AI->L); |
1012 | pos3L_register(AI->L); |
1013 | |
1014 | /* Register libraries. */ |
1015 | luaL_register(AI->L, AI_METATABLE"ai", AIL_methods); |
1016 | |
1017 | /* Load the AI */ |
1018 | Com_sprintf(path, sizeof(path), "ai/%s.lua", type); |
1019 | size = gi.FS_LoadFile(path, (byte **) &fbuf); |
1020 | if (size == 0) { |
1021 | gi.DPrintf("Unable to load Lua file '%s'.\n", path); |
1022 | return -1; |
1023 | } |
1024 | if (luaL_dobuffer(AI->L, fbuf, size, path)(luaL_loadbuffer(AI->L, fbuf, size, path) || lua_pcall(AI-> L, 0, (-1), 0))) { |
1025 | gi.DPrintf("Unable to parse Lua file '%s'\n", path); |
1026 | gi.FS_FreeFile(fbuf); |
1027 | return -1; |
1028 | } |
1029 | gi.FS_FreeFile(fbuf); |
1030 | |
1031 | return 0; |
1032 | } |
1033 | |
1034 | /** |
1035 | * @brief Cleans up the AI part of the actor. |
1036 | * @param[in] ent Pointer to actor to cleanup AI. |
1037 | */ |
1038 | static void AIL_CleanupActor (edict_t * ent) |
1039 | { |
1040 | AI_t *AI = &ent->AI; |
1041 | |
1042 | /* Cleanup. */ |
1043 | if (AI->L != NULL__null) { |
1044 | lua_close(AI->L); |
1045 | AI->L = NULL__null; |
1046 | } |
1047 | } |
1048 | |
1049 | void AIL_Init (void) |
1050 | { |
1051 | gi.RegisterConstInt("luaaiteam::phalanx", TEAM_PHALANX1); |
1052 | gi.RegisterConstInt("luaaiteam::civilian", TEAM_CIVILIAN0); |
1053 | gi.RegisterConstInt("luaaiteam::alien", TEAM_ALIEN7); |
1054 | gi.RegisterConstInt("luaaiteam::all", TEAM_ALL0xFFFFFFFF); |
1055 | } |
1056 | |
1057 | void AIL_Shutdown (void) |
1058 | { |
1059 | gi.UnregisterConstVariable("luaaiteam::phalanx"); |
1060 | gi.UnregisterConstVariable("luaaiteam::civilian"); |
1061 | gi.UnregisterConstVariable("luaaiteam::alien"); |
1062 | gi.UnregisterConstVariable("luaaiteam::all"); |
1063 | } |
1064 | |
1065 | /** |
1066 | * @brief Purges all the AI from the entities. |
1067 | */ |
1068 | void AIL_Cleanup (void) |
1069 | { |
1070 | edict_t *ent = NULL__null; |
1071 | |
1072 | while ((ent = G_EdictsGetNextActor(ent))) |
1073 | AIL_CleanupActor(ent); |
1074 | } |