1 /*****************************************************************************
2  Freeciv - Copyright (C) 2005 - The Freeciv Project
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; either version 2, or (at your option)
6    any later version.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 *****************************************************************************/
13 
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
17 
18 /* utility */
19 #include "rand.h"
20 
21 /* common */
22 #include "movement.h"
23 #include "research.h"
24 #include "unittype.h"
25 
26 /* common/scriptcore */
27 #include "api_game_find.h"
28 #include "luascript.h"
29 
30 /* server */
31 #include "aiiface.h"
32 #include "barbarian.h"
33 #include "citytools.h"
34 #include "console.h" /* enum rfc_status */
35 #include "maphand.h"
36 #include "notify.h"
37 #include "plrhand.h"
38 #include "srv_main.h" /* game_was_started() */
39 #include "stdinhand.h"
40 #include "techtools.h"
41 #include "unittools.h"
42 
43 /* server/scripting */
44 #include "script_server.h"
45 
46 #include "api_server_edit.h"
47 
48 
49 /*****************************************************************************
50   Unleash barbarians on a tile, for example from a hut
51 *****************************************************************************/
api_edit_unleash_barbarians(lua_State * L,Tile * ptile)52 bool api_edit_unleash_barbarians(lua_State *L, Tile *ptile)
53 {
54   LUASCRIPT_CHECK_STATE(L, FALSE);
55   LUASCRIPT_CHECK_ARG_NIL(L, ptile, 2, Tile, FALSE);
56 
57   return unleash_barbarians(ptile);
58 }
59 
60 /*****************************************************************************
61   Place partisans for a player around a tile (normally around a city).
62 *****************************************************************************/
api_edit_place_partisans(lua_State * L,Tile * ptile,Player * pplayer,int count,int sq_radius)63 void api_edit_place_partisans(lua_State *L, Tile *ptile, Player *pplayer,
64                               int count, int sq_radius)
65 {
66   LUASCRIPT_CHECK_STATE(L);
67   LUASCRIPT_CHECK_ARG_NIL(L, ptile, 2, Tile);
68   LUASCRIPT_CHECK_ARG_NIL(L, pplayer, 3, Player);
69   LUASCRIPT_CHECK_ARG(L, 0 <= sq_radius, 5, "radius must be positive");
70   LUASCRIPT_CHECK(L, 0 < num_role_units(L_PARTISAN),
71                   "no partisans in ruleset");
72 
73   return place_partisans(ptile, pplayer, count, sq_radius);
74 }
75 
76 /*****************************************************************************
77   Create a new unit.
78 *****************************************************************************/
api_edit_create_unit(lua_State * L,Player * pplayer,Tile * ptile,Unit_Type * ptype,int veteran_level,City * homecity,int moves_left)79 Unit *api_edit_create_unit(lua_State *L, Player *pplayer, Tile *ptile,
80                            Unit_Type *ptype, int veteran_level,
81                            City *homecity, int moves_left)
82 {
83   return api_edit_create_unit_full(L, pplayer, ptile, ptype, veteran_level,
84                                       homecity, moves_left, -1, NULL);
85 }
86 
87 /*****************************************************************************
88   Create a new unit.
89 *****************************************************************************/
api_edit_create_unit_full(lua_State * L,Player * pplayer,Tile * ptile,Unit_Type * ptype,int veteran_level,City * homecity,int moves_left,int hp_left,Unit * ptransport)90 Unit *api_edit_create_unit_full(lua_State *L, Player *pplayer, Tile *ptile,
91                                 Unit_Type *ptype, int veteran_level,
92                                 City *homecity, int moves_left, int hp_left,
93                                 Unit *ptransport)
94 {
95   struct fc_lua *fcl;
96   struct city *pcity;
97 
98   LUASCRIPT_CHECK_STATE(L, NULL);
99   LUASCRIPT_CHECK_ARG_NIL(L, pplayer, 2, Player, NULL);
100   LUASCRIPT_CHECK_ARG_NIL(L, ptile, 3, Tile, NULL);
101 
102   fcl = luascript_get_fcl(L);
103 
104   LUASCRIPT_CHECK(L, fcl != NULL, "Undefined Freeciv lua state!", NULL);
105 
106   if (ptype == NULL
107       || ptype < unit_type_array_first() || ptype > unit_type_array_last()) {
108     return NULL;
109   }
110 
111   if (ptransport) {
112     /* Extensive check to see if transport and unit are compatible */
113     int ret;
114     struct unit *pvirt = unit_virtual_create(pplayer, NULL, ptype,
115                                              veteran_level);
116     unit_tile_set(pvirt, ptile);
117     pvirt->homecity = homecity ? homecity->id : 0;
118     ret = can_unit_load(pvirt, ptransport);
119     unit_virtual_destroy(pvirt);
120     if (!ret) {
121       luascript_log(fcl, LOG_ERROR, "create_unit_full: '%s' cannot transport "
122                                     "'%s' here",
123                     utype_rule_name(unit_type_get(ptransport)),
124                     utype_rule_name(ptype));
125       return NULL;
126     }
127   } else if (!can_exist_at_tile(ptype, ptile)) {
128     luascript_log(fcl, LOG_ERROR, "create_unit_full: '%s' cannot exist at "
129                                   "tile", utype_rule_name(ptype));
130     return NULL;
131   }
132 
133   if (is_non_allied_unit_tile(ptile, pplayer)) {
134     luascript_log(fcl, LOG_ERROR, "create_unit_full: tile is occupied by "
135                                   "enemy unit");
136     return NULL;
137   }
138 
139   pcity = tile_city(ptile);
140   if (pcity != NULL && !pplayers_allied(pplayer, city_owner(pcity))) {
141     luascript_log(fcl, LOG_ERROR, "create_unit_full: tile is occupied by "
142                                   "enemy city");
143     return NULL;
144   }
145 
146   return create_unit_full(pplayer, ptile, ptype, veteran_level,
147                           homecity ? homecity->id : 0, moves_left,
148                           hp_left, ptransport);
149 }
150 
151 /*****************************************************************************
152   Teleport unit to destination tile
153 *****************************************************************************/
api_edit_unit_teleport(lua_State * L,Unit * punit,Tile * dest)154 bool api_edit_unit_teleport(lua_State *L, Unit *punit, Tile *dest)
155 {
156   bool alive;
157 
158   LUASCRIPT_CHECK_STATE(L, FALSE);
159   LUASCRIPT_CHECK_ARG_NIL(L, punit, 2, Unit, FALSE);
160   LUASCRIPT_CHECK_ARG_NIL(L, dest, 3, Tile, FALSE);
161 
162   /* Teleport first so destination is revealed even if unit dies */
163   alive = unit_move(punit, dest, 0, NULL);
164   if (alive) {
165     struct player *owner = unit_owner(punit);
166     struct city *pcity = tile_city(dest);
167 
168     if (!can_unit_exist_at_tile(punit, dest)) {
169       wipe_unit(punit, ULR_NONNATIVE_TERR, NULL);
170       return FALSE;
171     }
172     if (is_non_allied_unit_tile(dest, owner)
173         || (pcity && !pplayers_allied(city_owner(pcity), owner))) {
174       wipe_unit(punit, ULR_STACK_CONFLICT, NULL);
175       return FALSE;
176     }
177   }
178 
179   return alive;
180 }
181 
182 /*****************************************************************************
183   Change unit orientation
184 *****************************************************************************/
api_edit_unit_turn(lua_State * L,Unit * punit,Direction dir)185 void api_edit_unit_turn(lua_State *L, Unit *punit, Direction dir)
186 {
187   LUASCRIPT_CHECK_STATE(L);
188   LUASCRIPT_CHECK_ARG_NIL(L, punit, 2, Unit);
189 
190   if (direction8_is_valid(dir)) {
191     punit->facing = dir;
192 
193     send_unit_info(NULL, punit);
194   } else {
195     log_error("Illegal direction %d for unit from lua script", dir);
196   }
197 }
198 
199 /*****************************************************************************
200   Kill the unit.
201 *****************************************************************************/
api_edit_unit_kill(lua_State * L,Unit * punit,const char * reason,Player * killer)202 void api_edit_unit_kill(lua_State *L, Unit *punit, const char *reason,
203                         Player *killer)
204 {
205   enum unit_loss_reason loss_reason;
206 
207   LUASCRIPT_CHECK_STATE(L);
208   LUASCRIPT_CHECK_ARG_NIL(L, punit, 2, Unit);
209   LUASCRIPT_CHECK_ARG_NIL(L, reason, 3, string);
210 
211   loss_reason = unit_loss_reason_by_name(reason, fc_strcasecmp);
212 
213   LUASCRIPT_CHECK_ARG(L, unit_loss_reason_is_valid(loss_reason), 3,
214                       "Invalid unit loss reason");
215 
216   wipe_unit(punit, loss_reason, killer);
217 }
218 
219 /*****************************************************************************
220   Create a new city.
221 *****************************************************************************/
api_edit_create_city(lua_State * L,Player * pplayer,Tile * ptile,const char * name)222 void api_edit_create_city(lua_State *L, Player *pplayer, Tile *ptile,
223                           const char *name)
224 {
225   LUASCRIPT_CHECK_STATE(L);
226   LUASCRIPT_CHECK_ARG_NIL(L, pplayer, 2, Player);
227   LUASCRIPT_CHECK_ARG_NIL(L, ptile, 3, Tile);
228 
229   if (!name || name[0] == '\0') {
230     name = city_name_suggestion(pplayer, ptile);
231   }
232 
233   /* TODO: Allow initial citizen to be of nationality other than owner */
234   create_city(pplayer, ptile, name, pplayer);
235 }
236 
237 /*****************************************************************************
238   Create a new player.
239 *****************************************************************************/
api_edit_create_player(lua_State * L,const char * username,Nation_Type * pnation,const char * ai)240 Player *api_edit_create_player(lua_State *L, const char *username,
241                                Nation_Type *pnation, const char *ai)
242 {
243   struct player *pplayer = NULL;
244   char buf[128] = "";
245   struct fc_lua *fcl;
246 
247   LUASCRIPT_CHECK_STATE(L, NULL);
248   LUASCRIPT_CHECK_ARG_NIL(L, username, 2, string, NULL);
249   if (!ai) {
250     ai = default_ai_type_name();
251   }
252 
253   fcl = luascript_get_fcl(L);
254 
255   LUASCRIPT_CHECK(L, fcl != NULL, "Undefined Freeciv lua state!", NULL);
256 
257   if (game_was_started()) {
258     create_command_newcomer(username, ai, FALSE, pnation, &pplayer,
259                             buf, sizeof(buf));
260   } else {
261     create_command_pregame(username, ai, FALSE, &pplayer,
262                            buf, sizeof(buf));
263   }
264 
265   if (strlen(buf) > 0) {
266     luascript_log(fcl, LOG_NORMAL, "%s", buf);
267   }
268 
269   return pplayer;
270 }
271 
272 /*****************************************************************************
273   Change pplayer's gold by amount.
274 *****************************************************************************/
api_edit_change_gold(lua_State * L,Player * pplayer,int amount)275 void api_edit_change_gold(lua_State *L, Player *pplayer, int amount)
276 {
277   LUASCRIPT_CHECK_STATE(L);
278   LUASCRIPT_CHECK_ARG_NIL(L, pplayer, 2, Player);
279 
280   pplayer->economic.gold = MAX(0, pplayer->economic.gold + amount);
281 }
282 
283 /*****************************************************************************
284   Give pplayer technology ptech. Quietly returns NULL if
285   player already has this tech; otherwise returns the tech granted.
286   Use NULL for ptech to grant a random tech.
287   sends script signal "tech_researched" with the given reason
288 *****************************************************************************/
api_edit_give_technology(lua_State * L,Player * pplayer,Tech_Type * ptech,int cost,bool notify,const char * reason)289 Tech_Type *api_edit_give_technology(lua_State *L, Player *pplayer,
290                                     Tech_Type *ptech, int cost, bool notify,
291                                     const char *reason)
292 {
293   struct research *presearch;
294   Tech_type_id id;
295   Tech_Type *result;
296 
297   LUASCRIPT_CHECK_STATE(L, NULL);
298   LUASCRIPT_CHECK_ARG_NIL(L, pplayer, 2, Player, NULL);
299   LUASCRIPT_CHECK_ARG(L, cost >= -3, 4, "Unknown give_tech() cost value", NULL);
300 
301   presearch = research_get(pplayer);
302   if (ptech) {
303     id = advance_number(ptech);
304   } else {
305     id = pick_free_tech(presearch);
306   }
307 
308   if (is_future_tech(id)
309       || research_invention_state(presearch, id) != TECH_KNOWN) {
310     if (cost < 0) {
311       if (cost == -1) {
312         cost = game.server.freecost;
313       } else if (cost == -2) {
314         cost = game.server.conquercost;
315       } else if (cost == -3) {
316         cost = game.server.diplbulbcost;
317       } else {
318 
319         cost = 0;
320       }
321     }
322     research_apply_penalty(presearch, id, cost);
323     found_new_tech(presearch, id, FALSE, TRUE);
324     result = advance_by_number(id);
325     script_tech_learned(presearch, pplayer, result, reason);
326 
327     if (notify && result != NULL) {
328       const char *adv_name = research_advance_name_translation(presearch, id);
329       char research_name[MAX_LEN_NAME * 2];
330 
331       research_pretty_name(presearch, research_name, sizeof(research_name));
332 
333       notify_player(pplayer, NULL, E_TECH_GAIN, ftc_server,
334                     Q_("?fromscript:You acquire %s."), adv_name);
335       notify_research(presearch, pplayer, E_TECH_GAIN, ftc_server,
336                       /* TRANS: "The Greeks ..." or "The members of
337                        * team Red ..." */
338                       Q_("?fromscript:The %s acquire %s and share this "
339                          "advance with you."),
340                       nation_plural_for_player(pplayer), adv_name);
341       notify_research_embassies(presearch, NULL, E_TECH_EMBASSY, ftc_server,
342                                 /* TRANS: "The Greeks ..." or "The members of
343                                  * team Red ..." */
344                                 Q_("?fromscript:The %s acquire %s."),
345                                 research_name, adv_name);
346     }
347 
348     return result;
349   } else {
350     return NULL;
351   }
352 }
353 
354 /*****************************************************************************
355   Modify player's trait value.
356 *****************************************************************************/
api_edit_trait_mod_set(lua_State * L,Player * pplayer,const char * tname,const int mod)357 bool api_edit_trait_mod_set(lua_State *L, Player *pplayer,
358                             const char *tname, const int mod)
359 {
360   enum trait tr;
361 
362   LUASCRIPT_CHECK_STATE(L, -1);
363   LUASCRIPT_CHECK_ARG_NIL(L, pplayer, 2, Player, FALSE);
364   LUASCRIPT_CHECK_ARG_NIL(L, tname, 3, string, FALSE);
365 
366   tr = trait_by_name(tname, fc_strcasecmp);
367 
368   LUASCRIPT_CHECK_ARG(L, trait_is_valid(tr), 3, "no such trait", 0);
369 
370   pplayer->ai_common.traits[tr].mod += mod;
371 
372   return TRUE;
373 }
374 
375 /*****************************************************************************
376   Create a new extra.
377 *****************************************************************************/
api_edit_create_extra(lua_State * L,Tile * ptile,const char * name)378 void api_edit_create_extra(lua_State *L, Tile *ptile, const char *name)
379 {
380   struct extra_type *pextra;
381 
382   LUASCRIPT_CHECK_STATE(L);
383   LUASCRIPT_CHECK_ARG_NIL(L, ptile, 2, Tile);
384 
385   if (!name) {
386     return;
387   }
388 
389   pextra = extra_type_by_rule_name(name);
390 
391   if (pextra) {
392     create_extra(ptile, pextra, NULL);
393     update_tile_knowledge(ptile);
394   }
395 }
396 
397 /*****************************************************************************
398   Create a new base.
399 *****************************************************************************/
api_edit_create_base(lua_State * L,Tile * ptile,const char * name,Player * pplayer)400 void api_edit_create_base(lua_State *L, Tile *ptile, const char *name,
401                           Player *pplayer)
402 {
403   struct extra_type *pextra;
404 
405   LUASCRIPT_CHECK_STATE(L);
406   LUASCRIPT_CHECK_ARG_NIL(L, ptile, 2, Tile);
407 
408   if (!name) {
409     return;
410   }
411 
412   pextra = extra_type_by_rule_name(name);
413 
414   if (pextra != NULL && is_extra_caused_by(pextra, EC_BASE)) {
415     create_extra(ptile, pextra, pplayer);
416     update_tile_knowledge(ptile);
417   }
418 }
419 
420 /*****************************************************************************
421   Add a new road.
422 *****************************************************************************/
api_edit_create_road(lua_State * L,Tile * ptile,const char * name)423 void api_edit_create_road(lua_State *L, Tile *ptile, const char *name)
424 {
425   api_edit_create_extra(L, ptile, name);
426 }
427 
428 /*****************************************************************************
429   Remove extra from tile, if present
430 *****************************************************************************/
api_edit_remove_extra(lua_State * L,Tile * ptile,const char * name)431 void api_edit_remove_extra(lua_State *L, Tile *ptile, const char *name)
432 {
433   struct extra_type *pextra;
434 
435   LUASCRIPT_CHECK_STATE(L);
436   LUASCRIPT_CHECK_ARG_NIL(L, ptile, 2, Tile);
437 
438   if (!name) {
439     return;
440   }
441 
442   pextra = extra_type_by_rule_name(name);
443 
444   if (pextra != NULL && tile_has_extra(ptile, pextra)) {
445     tile_extra_rm_apply(ptile, pextra);
446     update_tile_knowledge(ptile);
447   }
448 }
449 
450 /*****************************************************************************
451   Set tile label text.
452 *****************************************************************************/
api_edit_tile_set_label(lua_State * L,Tile * ptile,const char * label)453 void api_edit_tile_set_label(lua_State *L, Tile *ptile, const char *label)
454 {
455   LUASCRIPT_CHECK_STATE(L);
456   LUASCRIPT_CHECK_SELF(L, ptile);
457   LUASCRIPT_CHECK_ARG_NIL(L, label, 3, string);
458 
459   tile_set_label(ptile, label);
460   if (server_state() >= S_S_RUNNING) {
461     send_tile_info(NULL, ptile, FALSE);
462   }
463 }
464 
465 /*****************************************************************************
466   Global climate change.
467 *****************************************************************************/
api_edit_climate_change(lua_State * L,enum climate_change_type type,int effect)468 void api_edit_climate_change(lua_State *L, enum climate_change_type type,
469                              int effect)
470 {
471   LUASCRIPT_CHECK_STATE(L);
472   LUASCRIPT_CHECK_ARG(L, type == CLIMATE_CHANGE_GLOBAL_WARMING
473                       || type == CLIMATE_CHANGE_NUCLEAR_WINTER,
474                       2, "invalid climate change type");
475   LUASCRIPT_CHECK_ARG(L, effect > 0, 3, "effect must be greater than zero");
476 
477   climate_change(type == CLIMATE_CHANGE_GLOBAL_WARMING, effect);
478 }
479 
480 /*****************************************************************************
481   Provoke a civil war.
482 *****************************************************************************/
api_edit_civil_war(lua_State * L,Player * pplayer,int probability)483 Player *api_edit_civil_war(lua_State *L, Player *pplayer, int probability)
484 {
485   LUASCRIPT_CHECK_STATE(L, NULL);
486   LUASCRIPT_CHECK_ARG_NIL(L, pplayer, 2, Player, NULL);
487   LUASCRIPT_CHECK_ARG(L, probability >= 0 && probability <= 100,
488                       3, "must be a percentage", NULL);
489 
490   if (!civil_war_possible(pplayer, FALSE, FALSE)) {
491     return NULL;
492   }
493 
494   if (probability == 0) {
495     /* Calculate chance with normal rules */
496     if (!civil_war_triggered(pplayer)) {
497       return NULL;
498     }
499   } else {
500     /* Fixed chance specified by script */
501     if (fc_rand(100) >= probability) {
502       return NULL;
503     }
504   }
505 
506   return civil_war(pplayer);
507 }
508 
509 /*****************************************************************************
510   Make player winner of the scenario
511 *****************************************************************************/
api_edit_player_victory(lua_State * L,Player * pplayer)512 void api_edit_player_victory(lua_State *L, Player *pplayer)
513 {
514   LUASCRIPT_CHECK_STATE(L);
515   LUASCRIPT_CHECK_SELF(L, pplayer);
516 
517   player_status_add(pplayer, PSTATUS_WINNER);
518 }
519 
520 /*****************************************************************************
521   Move a unit.
522 *****************************************************************************/
api_edit_unit_move(lua_State * L,Unit * punit,Tile * ptile,int movecost)523 bool api_edit_unit_move(lua_State *L, Unit *punit, Tile *ptile,
524                         int movecost)
525 {
526   LUASCRIPT_CHECK_STATE(L, FALSE);
527   LUASCRIPT_CHECK_SELF(L, punit, FALSE);
528   LUASCRIPT_CHECK_ARG_NIL(L, ptile, 3, Tile, FALSE);
529   LUASCRIPT_CHECK_ARG(L, movecost >= 0, 4, "Negative move cost!", FALSE);
530 
531   return unit_move(punit, ptile, movecost, NULL);
532 }
533 
534 /*****************************************************************************
535   Add history to a city
536 *****************************************************************************/
api_edit_city_add_history(lua_State * L,City * pcity,int amount)537 void api_edit_city_add_history(lua_State *L, City *pcity, int amount)
538 {
539   LUASCRIPT_CHECK_STATE(L);
540   LUASCRIPT_CHECK_SELF(L, pcity);
541 
542   pcity->history += amount;
543 }
544 
545 /*****************************************************************************
546   Add history to a player
547 *****************************************************************************/
api_edit_player_add_history(lua_State * L,Player * pplayer,int amount)548 void api_edit_player_add_history(lua_State *L, Player *pplayer, int amount)
549 {
550   LUASCRIPT_CHECK_STATE(L);
551   LUASCRIPT_CHECK_SELF(L, pplayer);
552 
553   pplayer->history += amount;
554 }
555