Project

General

Profile

Feature #1399 ยป lua-superspecialists-edit.patch

Alexandr Ignatiev, 06/05/2025 01:04 AM

View differences:

common/scriptcore/api_game_methods.c
return specialist_plural_translation(s);
}
/**********************************************************************//**
Return if specialist is a superspecialist
**************************************************************************/
bool api_methods_specialist_is_super(lua_State *L, Specialist *s)
{
LUASCRIPT_CHECK_STATE(L, FALSE);
LUASCRIPT_CHECK_SELF(L, s, FALSE);
return is_super_specialist(s);
}
/**********************************************************************//**
Return the native x coordinate of the tile.
**************************************************************************/
common/scriptcore/api_game_methods.h
const char *api_methods_specialist_rule_name(lua_State *L, Specialist *s);
const char *api_methods_specialist_name_translation(lua_State *L,
Specialist *s);
bool api_methods_specialist_is_super(lua_State *L, Specialist *s);
/* Tile */
int api_methods_tile_nat_x(lua_State *L, Tile *ptile);
common/scriptcore/tolua_game.pkg
/* Module Specialist */
module Specialist {
module properties {
bool api_methods_specialist_is_super
@ is_super (lua_State *L, Specialist *s);
}
const char *api_methods_specialist_rule_name
@ rule_name (lua_State *L, Specialist *self);
const char *api_methods_specialist_name_translation
server/cityturn.c
Increase city size by one. We do not refresh borders or send info about
the city to the clients as part of this function. There might be several
calls to this function at once, and those actions are needed only once.
If s is not supplied, adds a specialist respecting the city preferences
**************************************************************************/
static bool city_increase_size(struct city *pcity)
static bool city_increase_size(struct city *pcity, Specialist_type_id sid)
{
int new_food;
int savings_pct = city_growth_granary_savings(pcity);
......
}
pcity->food_stock = MIN(pcity->food_stock, new_food);
/* If there is enough food, and the city is big enough,
* make new citizens into scientists or taxmen -- Massimo */
if (sid >= 0) {
fc_assert_action(is_normal_specialist_id(sid), sid = DEFAULT_SPECIALIST);
pcity->specialists[sid]++;
} else {
/* If there is enough food, and the city is big enough,
* make new citizens into scientists or taxmen -- Massimo */
/* Ignore food if no square can be worked */
city_tile_iterate_skip_free_worked(nmap, city_map_radius_sq_get(pcity), pcenter,
ptile, _index, _x, _y) {
if (tile_worked(ptile) != pcity /* Quick test */
&& city_can_work_tile(pcity, ptile)) {
have_square = TRUE;
}
} city_tile_iterate_skip_free_worked_end;
/* Ignore food if no square can be worked */
city_tile_iterate_skip_free_worked(nmap, city_map_radius_sq_get(pcity), pcenter,
ptile, _index, _x, _y) {
if (tile_worked(ptile) != pcity /* Quick test */
&& city_can_work_tile(pcity, ptile)) {
have_square = TRUE;
if ((pcity->surplus[O_FOOD] >= 2 || !have_square)
&& is_city_option_set(pcity, CITYO_SCIENCE_SPECIALISTS)) {
pcity->specialists[best_specialist(O_SCIENCE, pcity)]++;
} else if ((pcity->surplus[O_FOOD] >= 2 || !have_square)
&& is_city_option_set(pcity, CITYO_GOLD_SPECIALISTS)) {
pcity->specialists[best_specialist(O_GOLD, pcity)]++;
} else {
pcity->specialists[DEFAULT_SPECIALIST]++; /* or else city is !sane */
}
} city_tile_iterate_skip_free_worked_end;
if ((pcity->surplus[O_FOOD] >= 2 || !have_square)
&& is_city_option_set(pcity, CITYO_SCIENCE_SPECIALISTS)) {
pcity->specialists[best_specialist(O_SCIENCE, pcity)]++;
} else if ((pcity->surplus[O_FOOD] >= 2 || !have_square)
&& is_city_option_set(pcity, CITYO_GOLD_SPECIALISTS)) {
pcity->specialists[best_specialist(O_GOLD, pcity)]++;
} else {
pcity->specialists[DEFAULT_SPECIALIST]++; /* or else city is !sane */
}
/* Deprecated signal. Connect your lua functions to "city_size_change" that's
......
/**********************************************************************//**
Do the city refresh after its size has increased, by any amount.
Any added citizens detected during check belong to nationality.
aaw means that the workers in the city will be auto-arranged.
**************************************************************************/
static void city_refresh_after_city_size_increase(struct city *pcity,
struct player *nationality)
struct player *nationality,
bool aaw)
{
struct player *powner = city_owner(pcity);
......
/* Refresh the city data; this also checks the squared city radius. */
city_refresh(pcity);
auto_arrange_workers(pcity);
if (aaw) {
auto_arrange_workers(pcity);
}
/* Update cities that have trade routes with us */
trade_partners_iterate(pcity, pcity2) {
......
/**********************************************************************//**
Change the city size. Return TRUE iff the city is still alive afterwards.
If the size increases, the new citizens belong to nationality.
If sid is negative, tries to take best specialist according to city setting
but most times overwrites this selection in following auto-arrangement.
reason for signal ("script" or nullptr)
**************************************************************************/
bool city_change_size(struct city *pcity, citizens size,
struct player *nationality, const char *reason)
struct player *nationality,
Specialist_type_id sid, const char *reason)
{
int change = size - city_size_get(pcity);
......
int id = pcity->id;
/* Increase city size until size reached, or increase fails */
while (size > current_size && city_increase_size(pcity)) {
while (size > current_size && city_increase_size(pcity, sid)) {
/* TODO: This is currently needed only because there's
* deprecated script signal "city_growth" emitted.
* Check the need after signal has been dropped completely. */
......
current_size++;
}
city_refresh_after_city_size_increase(pcity, nationality);
city_refresh_after_city_size_increase(pcity, nationality, sid < 0);
real_change = current_size - old_size;
......
} else {
bool success;
success = city_increase_size(pcity);
success = city_increase_size(pcity, -1);
map_claim_border(pcity->tile, pcity->owner, -1);
if (success) {
city_refresh_after_city_size_increase(pcity, nationality);
city_refresh_after_city_size_increase(pcity, nationality, TRUE);
script_server_signal_emit("city_size_change", pcity,
(lua_Integer)1, "growth");
}
......
/* Increase size of receiver city */
if (city_exist(to_id)) {
bool incr_success = city_increase_size(pcity_to);
bool incr_success = city_increase_size(pcity_to, -1);
if (city_exist(to_id)) {
city_refresh_after_city_size_increase(pcity_to, pplayer_citizen);
city_refresh_after_city_size_increase(pcity_to, pplayer_citizen, TRUE);
city_refresh_vision(pcity_to);
if (city_refresh(pcity_to)) {
auto_arrange_workers(pcity_to);
server/cityturn.h
void auto_arrange_workers(struct city *pcity); /* Will arrange the workers */
void apply_cmresult_to_city(struct city *pcity, const struct cm_result *cmr);
bool city_change_size(struct city *pcity, citizens new_size,
struct player *nationality, const char *reason);
bool city_change_size(struct city *pcity, citizens size,
struct player *nationality,
Specialist_type_id sid, const char *reason);
bool city_reduce_size(struct city *pcity, citizens pop_loss,
struct player *destroyer, const char *reason);
void city_repair_size(struct city *pcity, int change);
server/edithand.c
if (size > 1) {
/* FIXME: Slow and inefficient for large size changes. */
city_change_size(pcity, CLIP(1, size, MAX_CITY_SIZE), pplayer, NULL);
city_change_size(pcity, CLIP(1, size, MAX_CITY_SIZE), pplayer,
-1, nullptr);
send_city_info(NULL, pcity);
}
......
packet->size, city_link(pcity));
} else {
/* FIXME: Slow and inefficient for large size changes. */
city_change_size(pcity, packet->size, NULL, NULL);
city_change_size(pcity, packet->size, nullptr, -1, nullptr);
changed = TRUE;
}
}
server/scripting/api_server_edit.c
}
/**********************************************************************//**
Reduce specialists of given type s in a way like toggling in the client.
Reduce specialists of given type s. Superspecialists are just reduced,
normal specialists are toggled in a way like toggling in the client.
Does not place workers on map, just switches to another specialist.
Does nothing if there is less than amount specialists s in pcity.
Return if given number could be repurposed.
Return if given number could be removed/repurposed.
**************************************************************************/
bool api_edit_city_reduce_specialists(lua_State *L, City *pcity,
Specialist *s, int amount)
{
Specialist_type_id from, to;
Specialist_type_id from;
LUASCRIPT_CHECK_STATE(L, FALSE);
LUASCRIPT_CHECK_SELF(L, pcity, FALSE);
......
if (pcity->specialists[from] < amount) {
return FALSE;
}
to = from;
do {
to = (to + 1) % specialist_count();
} while (to != from && !city_can_use_specialist(pcity, to));
if (to == from) {
/* We can use only the default specialist */
return FALSE;
} else {
/* City population must be correct */
fc_assert_ret_val_msg(pcity->specialists[to] <= MAX_CITY_SIZE - amount,
FALSE, "Wrong specialist number in %s",
city_name_get(pcity));
if (is_super_specialist_id(from)) {
/* Just reduce superspecialists */
pcity->specialists[from] -= amount;
pcity->specialists[to] += amount;
} else {
/* Toggle normal specialist */
Specialist_type_id to = from;
do {
to = (to + 1) % normal_specialist_count();
} while (to != from && !city_can_use_specialist(pcity, to));
if (to == from) {
/* We can use only the default specialist */
return FALSE;
} else {
/* City population must be correct */
fc_assert_ret_val_msg(pcity->specialists[to] <= amount - MAX_CITY_SIZE,
FALSE, "Wrong specialist number in %s",
city_name_get(pcity));
pcity->specialists[from] -= amount;
pcity->specialists[to] += amount;
}
}
city_refresh(pcity);
/* sanity_check_city(pcity); -- hopefully we don't break things here? */
send_city_info(city_owner(pcity), pcity);
return TRUE;
}
/**********************************************************************//**
Add amount specialists of given type s to pcity, return true iff done.
For normal specialists, also increases city size at amount.
Fails if either pcity does not fulfill s->reqs or it does not have
enough space for given specialists or citizens number.
**************************************************************************/
bool api_edit_city_add_specialist(lua_State *L, City *pcity,
Specialist *s, int amount)
{
Specialist_type_id sid;
int csize = 0;
LUASCRIPT_CHECK_STATE(L, FALSE);
LUASCRIPT_CHECK_SELF(L, pcity, FALSE);
LUASCRIPT_CHECK_ARG_NIL(L, s, 2, Specialist, FALSE);
LUASCRIPT_CHECK_ARG(L, amount >= 0, 3, "must be non-negative", FALSE);
sid = specialist_index(s);
if (!city_can_use_specialist(pcity, sid)) {
/* Can't employ this one */
return FALSE;
}
if (is_super_specialist(s)) {
if (pcity->specialists[sid] > MAX_CITY_SIZE - amount) {
/* No place for the specialist */
return FALSE;
}
pcity->specialists[sid] += amount;
city_refresh(pcity);
/* sanity_check_city(pcity); -- hopefully we don't break things here? */
send_city_info(city_owner(pcity), pcity);
} else {
csize = city_size_get(pcity);
return TRUE;
if (csize > MAX_CITY_SIZE - amount) {
/* No place for the specialist */
return FALSE;
}
city_change_size(pcity, csize + amount, city_owner(pcity), sid, "script");
city_refresh(pcity);
send_city_info(nullptr, pcity);
}
return TRUE;
}
/**********************************************************************//**
......
nationality = city_owner(pcity);
}
city_change_size(pcity, city_size_get(pcity) + change, nationality, "script");
city_change_size(pcity, city_size_get(pcity) + change, nationality,
-1, "script");
}
/**********************************************************************//**
server/scripting/api_server_edit.h
void api_edit_remove_building(lua_State *L, City *pcity, Building_Type *impr);
bool api_edit_city_reduce_specialists(lua_State *L, City *pcity,
Specialist *s, int amount);
bool api_edit_city_add_specialist(lua_State *L, City *pcity,
Specialist *s, int amount);
Player *api_edit_create_player(lua_State *L, const char *username,
Nation_Type *pnation, const char *ai);
void api_edit_change_gold(lua_State *L, Player *pplayer, int amount);
server/scripting/tolua_server.pkg
bool api_edit_city_reduce_specialists
@ reduce_specialists (lua_State *L, City *pcity, Specialist *s,
int amount = 1);
bool api_edit_city_add_specialist
@ add_specialist (lua_State *L, City *pcity, Specialist *s,
int amount = 1);
void api_edit_create_owned_extra
@ create_owned_extra (lua_State *L, Tile *ptile,
const char *name, Player *pplayer);
......
return edit.reduce_specialists(self, spec, amount or 1)
end
function City:add_specialist(spec, amount)
return edit.add_specialist(self, spec, amount or 1)
end
function City:change_size(change, nationality)
edit.change_city_size(self, change, nationality)
end
server/unithand.c
fc_assert_ret_val(pcity != NULL, FALSE);
city_change_size(pcity, size, nationality, NULL);
city_change_size(pcity, size, nationality, -1, nullptr);
}
/* May cause an incident even if the target tile is unclaimed. A ruleset
    (1-1/1)