00001
00002
00003
00004
00005
00006
00007
00008
00009
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00030
00031
00032
00033
00034
00035 #include <stdio.h>
00036 #include <stdlib.h>
00037 #include <string.h>
00038 #include <setjmp.h>
00039
00040 #include "stratagus.h"
00041 #include "script.h"
00042 #include "unittype.h"
00043 #include "unit_cache.h"
00044 #include "player.h"
00045 #include "trigger.h"
00046 #include "results.h"
00047 #include "interface.h"
00048 #include "unit.h"
00049 #include "iolib.h"
00050
00051
00052
00053
00054
00055
00056 CTimer GameTimer;
00057 static int Trigger;
00058 static bool *ActiveTriggers;
00059
00061 TriggerDataType TriggerData;
00062
00063
00064
00065
00066
00067
00075 int TriggerGetPlayer(lua_State *l)
00076 {
00077 const char *player;
00078 int ret;
00079
00080 if (lua_isnumber(l, -1)) {
00081 ret = LuaToNumber(l, -1);
00082 if (ret < 0 || ret > PlayerMax) {
00083 LuaError(l, "bad player: %d" _C_ ret);
00084 }
00085 return ret;
00086 }
00087 player = LuaToString(l, -1);
00088 if (!strcmp(player, "any")) {
00089 return -1;
00090 } else if (!strcmp(player, "this")) {
00091 return ThisPlayer->Index;
00092 }
00093 LuaError(l, "bad player: %s" _C_ player);
00094
00095 return 0;
00096 }
00097
00105 const CUnitType *TriggerGetUnitType(lua_State *l)
00106 {
00107 const char *unit;
00108
00109 unit = LuaToString(l, -1);
00110 if (!strcmp(unit, "any")) {
00111 return ANY_UNIT;
00112 } else if (!strcmp(unit, "all")) {
00113 return ALL_UNITS;
00114 } else if (!strcmp(unit, "units")) {
00115 return ALL_FOODUNITS;
00116 } else if (!strcmp(unit, "buildings")) {
00117 return ALL_BUILDINGS;
00118 }
00119
00120 return CclGetUnitType(l);
00121 }
00122
00123
00124
00125
00126
00127 static int CompareEq(int a, int b)
00128 {
00129 return a == b;
00130 }
00131 static int CompareNEq(int a, int b)
00132 {
00133 return a != b;
00134 }
00135 static int CompareGrEq(int a, int b)
00136 {
00137 return a >= b;
00138 }
00139 static int CompareGr(int a, int b)
00140 {
00141 return a > b;
00142 }
00143 static int CompareLeEq(int a, int b)
00144 {
00145 return a <= b;
00146 }
00147 static int CompareLe(int a, int b)
00148 {
00149 return a < b;
00150 }
00151
00152 typedef int (*CompareFunction)(int, int);
00153
00161 static CompareFunction GetCompareFunction(const char *op)
00162 {
00163 if (op[0] == '=') {
00164 if ((op[1] == '=' && op[2] == '\0') || (op[1] == '\0')) {
00165 return &CompareEq;
00166 }
00167 } else if (op[0] == '>') {
00168 if (op[1] == '=' && op[2] == '\0') {
00169 return &CompareGrEq;
00170 } else if (op[1] == '\0') {
00171 return &CompareGr;
00172 }
00173 } else if (op[0] == '<') {
00174 if (op[1] == '=' && op[2] == '\0') {
00175 return &CompareLeEq;
00176 } else if (op[1] == '\0') {
00177 return &CompareLe;
00178 }
00179 } else if (op[0] == '!' && op[1] == '=' && op[2] == '\0') {
00180 return &CompareNEq;
00181 }
00182 return NULL;
00183 }
00184
00188 static int CclGetNumUnitsAt(lua_State *l)
00189 {
00190 int plynr;
00191 int x1;
00192 int y1;
00193 int x2;
00194 int y2;
00195 const CUnitType *unittype;
00196 CUnit *table[UnitMax];
00197 CUnit *unit;
00198 int an;
00199 int j;
00200 int s;
00201
00202 LuaCheckArgs(l, 4);
00203
00204 plynr = LuaToNumber(l, 1);
00205 lua_pushvalue(l, 2);
00206 unittype = TriggerGetUnitType(l);
00207 lua_pop(l, 1);
00208 if (!lua_istable(l, 3) || lua_objlen(l, 3) != 2) {
00209 LuaError(l, "incorrect argument");
00210 }
00211 lua_rawgeti(l, 3, 1);
00212 x1 = LuaToNumber(l, -1);
00213 lua_pop(l, 1);
00214 lua_rawgeti(l, 3, 2);
00215 y1 = LuaToNumber(l, -1);
00216 lua_pop(l, 1);
00217 if (!lua_istable(l, 4) || lua_objlen(l, 4) != 2) {
00218 LuaError(l, "incorrect argument");
00219 }
00220 lua_rawgeti(l, 4, 1);
00221 x2 = LuaToNumber(l, -1);
00222 lua_pop(l, 1);
00223 lua_rawgeti(l, 4, 2);
00224 y2 = LuaToNumber(l, -1);
00225 lua_pop(l, 1);
00226
00227
00228
00229
00230 an = UnitCache.Select(x1, y1, x2 + 1, y2 + 1, table, UnitMax);
00231
00232
00233
00234 for (j = s = 0; j < an; ++j) {
00235 unit = table[j];
00236
00237
00238
00239
00240 if (unittype == ANY_UNIT ||
00241 (unittype == ALL_FOODUNITS && !unit->Type->Building) ||
00242 (unittype == ALL_BUILDINGS && unit->Type->Building) ||
00243 (unittype == unit->Type && !unit->Constructed)) {
00244
00245
00246
00247 if (plynr == -1 || plynr == unit->Player->Index) {
00248 ++s;
00249 }
00250 }
00251 }
00252 lua_pushnumber(l, s);
00253 return 1;
00254 }
00255
00259 static int CclIfNearUnit(lua_State *l)
00260 {
00261 int plynr;
00262 int q;
00263 int n;
00264 int i;
00265 const CUnitType *unittype;
00266 const CUnitType *ut2;
00267 const char *op;
00268 CUnit *table[UnitMax];
00269 CompareFunction compare;
00270
00271 LuaCheckArgs(l, 5);
00272
00273 lua_pushvalue(l, 1);
00274 plynr = TriggerGetPlayer(l);
00275 lua_pop(l, 1);
00276 op = LuaToString(l, 2);
00277 q = LuaToNumber(l, 3);
00278 lua_pushvalue(l, 4);
00279 unittype = TriggerGetUnitType(l);
00280 lua_pop(l, 1);
00281 ut2 = CclGetUnitType(l);
00282
00283 compare = GetCompareFunction(op);
00284 if (!compare) {
00285 LuaError(l, "Illegal comparison operation in if-near-unit: %s" _C_ op);
00286 }
00287
00288
00289
00290
00291 n = FindUnitsByType(ut2, table, UnitMax);
00292 for (i = 0; i < n; ++i) {
00293 CUnit *unit;
00294 CUnit *around[UnitMax];
00295 int an;
00296 int j;
00297 int s;
00298
00299 unit = table[i];
00300
00301 if (unit->Type->UnitType == UnitTypeLand) {
00302 an = UnitCache.Select(unit->X - 1, unit->Y - 1,
00303 unit->X + unit->Type->TileWidth + 1,
00304 unit->Y + unit->Type->TileHeight + 1, around, UnitMax);
00305 } else {
00306 an = UnitCache.Select(unit->X - 2, unit->Y - 2,
00307 unit->X + unit->Type->TileWidth + 2,
00308 unit->Y + unit->Type->TileHeight + 2, around, UnitMax);
00309 }
00310
00311
00312
00313 for (j = s = 0; j < an; ++j) {
00314 unit = around[j];
00315
00316
00317
00318
00319 if (unittype == ANY_UNIT ||
00320 (unittype == ALL_FOODUNITS && !unit->Type->Building) ||
00321 (unittype == ALL_BUILDINGS && unit->Type->Building) ||
00322 (unittype == unit->Type)) {
00323
00324
00325
00326 if (plynr == -1 || plynr == unit->Player->Index) {
00327 ++s;
00328 }
00329 }
00330 }
00331
00332 if (unittype == ANY_UNIT ||
00333 (unittype == ALL_FOODUNITS && ut2->Building) ||
00334 (unittype == ALL_BUILDINGS && ut2->Building)) {
00335 --s;
00336 }
00337 if (compare(s, q)) {
00338 lua_pushboolean(l, 1);
00339 return 1;
00340 }
00341 }
00342
00343 lua_pushboolean(l, 0);
00344 return 1;
00345 }
00346
00350 static int CclIfRescuedNearUnit(lua_State *l)
00351 {
00352 int plynr;
00353 int q;
00354 int n;
00355 int i;
00356 const CUnitType *unittype;
00357 const CUnitType *ut2;
00358 const char *op;
00359 CUnit *table[UnitMax];
00360 CompareFunction compare;
00361
00362 LuaCheckArgs(l, 5);
00363
00364 lua_pushvalue(l, 1);
00365 plynr = TriggerGetPlayer(l);
00366 lua_pop(l, 1);
00367 op = LuaToString(l, 2);
00368 q = LuaToNumber(l, 3);
00369 lua_pushvalue(l, 4);
00370 unittype = TriggerGetUnitType(l);
00371 lua_pop(l, 1);
00372 ut2 = CclGetUnitType(l);
00373
00374 compare = GetCompareFunction(op);
00375 if (!compare) {
00376 LuaError(l, "Illegal comparison operation in if-rescued-near-unit: %s" _C_ op);
00377 }
00378
00379
00380
00381
00382 n = FindUnitsByType(ut2, table, UnitMax);
00383 for (i = 0; i < n; ++i) {
00384 CUnit *unit;
00385 CUnit *around[UnitMax];
00386 int an;
00387 int j;
00388 int s;
00389
00390 unit = table[i];
00391
00392 if (unit->Type->UnitType == UnitTypeLand) {
00393 an = UnitCache.Select(unit->X - 1, unit->Y - 1,
00394 unit->X + unit->Type->TileWidth + 1,
00395 unit->Y + unit->Type->TileHeight + 1, around, UnitMax);
00396 } else {
00397 an = UnitCache.Select(unit->X - 2, unit->Y - 2,
00398 unit->X + unit->Type->TileWidth + 2,
00399 unit->Y + unit->Type->TileHeight + 2, around, UnitMax);
00400 }
00401
00402
00403
00404 for (j = s = 0; j < an; ++j) {
00405 unit = around[j];
00406 if (unit->RescuedFrom) {
00407
00408
00409
00410
00411 if (unittype == ANY_UNIT ||
00412 (unittype == ALL_FOODUNITS && !unit->Type->Building) ||
00413 (unittype == ALL_BUILDINGS && unit->Type->Building) ||
00414 (unittype == unit->Type)) {
00415
00416
00417
00418 if (plynr == -1 || plynr == unit->Player->Index) {
00419 ++s;
00420 }
00421 }
00422 }
00423 }
00424
00425 if (unittype == ANY_UNIT ||
00426 (unittype == ALL_FOODUNITS && ut2->Building) ||
00427 (unittype == ALL_BUILDINGS && ut2->Building)) {
00428 --s;
00429 }
00430 if (compare(s, q)) {
00431 lua_pushboolean(l, 1);
00432 return 1;
00433 }
00434 }
00435
00436 lua_pushboolean(l, 0);
00437 return 1;
00438 }
00439
00443 int GetNumOpponents(int player)
00444 {
00445 int n = 0;
00446
00447
00448 for (int i = 0; i < PlayerMax; ++i) {
00449
00450 if (((Players[player].Enemy & (1 << i)) || (Players[i].Enemy & (1 << player))) &&
00451 Players[i].TotalNumUnits) {
00452 ++n;
00453 }
00454 }
00455
00456 return n;
00457 }
00458
00462 int GetTimer()
00463 {
00464 if (!GameTimer.Init) {
00465 return 0;
00466 }
00467 return GameTimer.Cycles;
00468 }
00469
00470
00471
00472
00473
00477 void StopGame(GameResults result)
00478 {
00479 GameResult = result;
00480 GamePaused = true;
00481 GameRunning = false;
00482 }
00483
00487 void ActionVictory()
00488 {
00489 StopGame(GameVictory);
00490 }
00491
00495 void ActionDefeat()
00496 {
00497 StopGame(GameDefeat);
00498 }
00499
00503 void ActionDraw()
00504 {
00505 StopGame(GameDraw);
00506 }
00507
00511 void ActionSetTimer(int cycles, bool increasing)
00512 {
00513 GameTimer.Cycles = cycles;
00514 GameTimer.Increasing = increasing;
00515 GameTimer.Init = true;
00516 GameTimer.LastUpdate = GameCycle;
00517 }
00518
00522 void ActionStartTimer()
00523 {
00524 GameTimer.Running = true;
00525 GameTimer.Init = true;
00526 }
00527
00531 void ActionStopTimer()
00532 {
00533 GameTimer.Running = false;
00534 }
00535
00539 static int CclAddTrigger(lua_State *l)
00540 {
00541 int i;
00542
00543 LuaCheckArgs(l, 2);
00544 if (!lua_isfunction(l, 1) ||
00545 (!lua_isfunction(l, 2) && !lua_istable(l, 2))) {
00546 LuaError(l, "incorrect argument");
00547 }
00548
00549
00550
00551
00552
00553 lua_pushstring(l, "_triggers_");
00554 lua_gettable(l, LUA_GLOBALSINDEX);
00555
00556 if (lua_isnil(l, -1)) {
00557 puts("Trigger not set, defining trigger");
00558 lua_pop(l, 1);
00559 lua_pushstring(l, "_triggers_");
00560 lua_newtable(l);
00561 lua_settable(l, LUA_GLOBALSINDEX);
00562 lua_pushstring(l, "_triggers_");
00563 lua_gettable(l, LUA_GLOBALSINDEX);
00564 }
00565
00566 i = lua_objlen(l, -1);
00567 if (ActiveTriggers && !ActiveTriggers[i / 2]) {
00568 lua_pushnil(l);
00569 lua_rawseti(l, -2, i + 1);
00570 lua_pushnil(l);
00571 lua_rawseti(l, -2, i + 2);
00572 } else {
00573 lua_pushvalue(l, 1);
00574 lua_rawseti(l, -2, i + 1);
00575 lua_newtable(l);
00576 lua_pushvalue(l, 2);
00577 lua_rawseti(l, -2, 1);
00578 lua_rawseti(l, -2, i + 2);
00579 }
00580 lua_pop(l, 1);
00581
00582 return 0;
00583 }
00584
00588 void SetTrigger(int trigger)
00589 {
00590 Trigger = trigger;
00591 }
00592
00596 static int CclSetActiveTriggers(lua_State *l)
00597 {
00598 int args;
00599
00600 args = lua_gettop(l);
00601 ActiveTriggers = new bool[args];
00602 for (int j = 0; j < args; ++j) {
00603 ActiveTriggers[j] = LuaToBoolean(l, j + 1);
00604 }
00605
00606 return 0;
00607 }
00608
00616 static int TriggerExecuteAction(int script)
00617 {
00618 int ret;
00619 int args;
00620 int j;
00621 int base = lua_gettop(Lua);
00622
00623 ret = 0;
00624
00625 lua_rawgeti(Lua, -1, script + 1);
00626 args = lua_objlen(Lua, -1);
00627 for (j = 0; j < args; ++j) {
00628 lua_rawgeti(Lua, -1, j + 1);
00629 LuaCall(0, 0);
00630 if (lua_gettop(Lua) > base + 1 && lua_toboolean(Lua, -1)) {
00631 ret = 1;
00632 } else {
00633 ret = 0;
00634 }
00635 lua_settop(Lua, base + 1);
00636 }
00637 lua_pop(Lua, 1);
00638
00639
00640 return !ret;
00641 }
00642
00648 static void TriggerRemoveTrigger(int trig)
00649 {
00650 lua_pushnumber(Lua, -1);
00651 lua_rawseti(Lua, -2, trig + 1);
00652 lua_pushnumber(Lua, -1);
00653 lua_rawseti(Lua, -2, trig + 2);
00654 }
00655
00659 void TriggersEachCycle(void)
00660 {
00661 int triggers;
00662 int base = lua_gettop(Lua);
00663
00664 lua_pushstring(Lua, "_triggers_");
00665 lua_gettable(Lua, LUA_GLOBALSINDEX);
00666 triggers = lua_objlen(Lua, -1);
00667
00668 if (Trigger >= triggers) {
00669 Trigger = 0;
00670 }
00671
00672 if (GamePaused) {
00673 lua_pop(Lua, 1);
00674 return;
00675 }
00676
00677
00678 while (Trigger < triggers) {
00679 lua_rawgeti(Lua, -1, Trigger + 1);
00680 if (!lua_isnumber(Lua, -1)) {
00681 break;
00682 }
00683 lua_pop(Lua, 1);
00684 Trigger += 2;
00685 }
00686 if (Trigger < triggers) {
00687 int currentTrigger = Trigger;
00688 Trigger += 2;
00689 LuaCall(0, 0);
00690
00691 if (lua_gettop(Lua) > base + 1 && lua_toboolean(Lua, -1)) {
00692 lua_settop(Lua, base + 1);
00693 if (TriggerExecuteAction(currentTrigger + 1)) {
00694 TriggerRemoveTrigger(currentTrigger);
00695 }
00696 }
00697 lua_settop(Lua, base + 1);
00698 }
00699 lua_pop(Lua, 1);
00700 }
00701
00705 void TriggerCclRegister(void)
00706 {
00707 lua_register(Lua, "AddTrigger", CclAddTrigger);
00708 lua_register(Lua, "SetActiveTriggers", CclSetActiveTriggers);
00709
00710 lua_register(Lua, "GetNumUnitsAt", CclGetNumUnitsAt);
00711 lua_register(Lua, "IfNearUnit", CclIfNearUnit);
00712 lua_register(Lua, "IfRescuedNearUnit", CclIfRescuedNearUnit);
00713 }
00714
00720 void SaveTriggers(CFile *file)
00721 {
00722 int i;
00723 int triggers;
00724
00725 lua_pushstring(Lua, "_triggers_");
00726 lua_gettable(Lua, LUA_GLOBALSINDEX);
00727 triggers = lua_objlen(Lua, -1);
00728
00729 file->printf("SetActiveTriggers(");
00730 for (i = 0; i < triggers; i += 2) {
00731 lua_rawgeti(Lua, -1, i + 1);
00732 if (i) {
00733 file->printf(", ");
00734 }
00735 if (!lua_isnil(Lua, -1)) {
00736 file->printf("true");
00737 } else {
00738 file->printf("false");
00739 }
00740 lua_pop(Lua, 1);
00741 }
00742 file->printf(")\n");
00743
00744 file->printf("SetTrigger(%d)\n", Trigger);
00745
00746 if (GameTimer.Init) {
00747 file->printf("ActionSetTimer(%ld, %s)\n",
00748 GameTimer.Cycles, (GameTimer.Increasing ? "true" : "false"));
00749 if (GameTimer.Running) {
00750 file->printf("ActionStartTimer()\n");
00751 }
00752 }
00753 }
00754
00758 void InitTriggers(void)
00759 {
00760
00761
00762
00763
00764
00765 lua_pushstring(Lua, "_triggers_");
00766 lua_gettable(Lua, LUA_GLOBALSINDEX);
00767 if (lua_isnil(Lua, -1)) {
00768 lua_pushstring(Lua, "SinglePlayerTriggers");
00769 lua_gettable(Lua, LUA_GLOBALSINDEX);
00770 LuaCall(0, 1);
00771 }
00772 lua_pop(Lua, 1);
00773 }
00774
00778 void CleanTriggers(void)
00779 {
00780 lua_pushstring(Lua, "_triggers_");
00781 lua_pushnil(Lua);
00782 lua_settable(Lua, LUA_GLOBALSINDEX);
00783
00784 Trigger = 0;
00785
00786 delete[] ActiveTriggers;
00787 ActiveTriggers = NULL;
00788
00789 GameTimer.Reset();
00790 }
00791