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