mirror of
https://github.com/SWG-Source/dsrc.git
synced 2026-07-28 22:15:54 -04:00
866 lines
28 KiB
Plaintext
866 lines
28 KiB
Plaintext
// ======================================================================
|
|
//
|
|
// master_city_object.script
|
|
//
|
|
// Copyright 2003 Sony Online Entertainment
|
|
//
|
|
// ======================================================================
|
|
|
|
include library.city;
|
|
include library.prose;
|
|
include library.utils;
|
|
include library.money;
|
|
include library.xp;
|
|
include library.objvar_mangle;
|
|
include library.player_structure;
|
|
|
|
include library.debug;
|
|
|
|
//------------------------------------------------
|
|
// Constants
|
|
//------------------------------------------------
|
|
|
|
const int CITY_PROCESS_INTERVAL = 60*60; // 1 hour
|
|
|
|
const int CITY_UPDATE_INTERVAL = 60*60*24*7; // 1 week
|
|
|
|
const string_id ELECTION_INCUMBENT_WIN_SUBJECT = new string_id( "city/city", "election_incumbent_win_subject" );
|
|
const string_id ELECTION_INCUMBENT_WIN_BODY = new string_id( "city/city", "election_incumbent_win_body" );
|
|
|
|
const string_id ELECTION_INCUMBENT_LOST_SUBJECT = new string_id( "city/city", "election_incumbent_lost_subject" );
|
|
const string_id ELECTION_INCUMBENT_LOST_BODY = new string_id( "city/city", "election_incumbent_lost_body" );
|
|
|
|
const string_id ELECTION_NEW_MAYOR_SUBJECT = new string_id( "city/city", "election_new_mayor_subject" );
|
|
const string_id ELECTION_NEW_MAYOR_BODY = new string_id( "city/city", "election_new_mayor_body" );
|
|
|
|
const string_id CITY_EXPAND_SUBJECT = new string_id( "city/city", "city_expand_subject" );
|
|
const string_id CITY_EXPAND_BODY = new string_id( "city/city", "city_expand_body" );
|
|
|
|
const string_id CITY_CONTRACT_SUBJECT = new string_id( "city/city", "city_contract_subject" );
|
|
const string_id CITY_CONTRACT_BODY = new string_id( "city/city", "city_contract_body" );
|
|
|
|
const string_id CITY_INVALID_SUBJECT = new string_id( "city/city", "city_invalid_subject" );
|
|
const string_id CITY_INVALID_BODY = new string_id( "city/city", "city_invalid_body" );
|
|
|
|
const string_id CITY_EXPAND_CAP_SUBJECT = new string_id( "city/city", "city_expand_cap_subject" );
|
|
const string_id CITY_EXPAND_CAP_BODY = new string_id( "city/city", "city_expand_cap_body" );
|
|
|
|
const string_id PUBLIC_ELECTION_INC_SUBJECT = new string_id( "city/city", "public_election_inc_subject" );
|
|
const string_id PUBLIC_ELECTION_INC_BODY = new string_id( "city/city", "public_election_inc_body" );
|
|
const string_id PUBLIC_ELECTION_SUBJECT = new string_id( "city/city", "public_election_subject" );
|
|
const string_id PUBLIC_ELECTION_BODY = new string_id( "city/city", "public_election_body" );
|
|
|
|
const string_id SID_CITY_UPDATE_ETA = new string_id( "city/city", "city_update_eta" );
|
|
|
|
//------------------------------------------------
|
|
// OnInitialize
|
|
//------------------------------------------------
|
|
|
|
trigger OnInitialize()
|
|
{
|
|
messageTo(self, "onCityUpdatePulse", null, CITY_PROCESS_INTERVAL, false);
|
|
return SCRIPT_CONTINUE;
|
|
}
|
|
|
|
//------------------------------------------------
|
|
// onCityUpdatePulse
|
|
//------------------------------------------------
|
|
|
|
messageHandler onCityUpdatePulse()
|
|
{
|
|
CustomerServiceLog("player_city", "--- City Update ---");
|
|
|
|
// Update all cities.
|
|
updateCities(self, false);
|
|
|
|
// Queue next update.
|
|
messageTo(self, "onCityUpdatePulse", null, CITY_PROCESS_INTERVAL, false);
|
|
|
|
return SCRIPT_CONTINUE;
|
|
}
|
|
|
|
//------------------------------------------------
|
|
// updateCities
|
|
//------------------------------------------------
|
|
|
|
void updateCities(obj_id self, boolean force)
|
|
{
|
|
int[] cityUpdateIds = objvar_mangle.getMangledIntArrayObjVar(self, "cityUpdate.ids");
|
|
int[] cityUpdateTimes = objvar_mangle.getMangledIntArrayObjVar(self, "cityUpdate.times");
|
|
|
|
string strCityUpdateInterval = getConfigSetting("GameServer", "cityUpdateInterval");
|
|
int cityUpdateInterval = 0;
|
|
|
|
if(strCityUpdateInterval != null)
|
|
cityUpdateInterval = utils.stringToInt(strCityUpdateInterval);
|
|
|
|
if(cityUpdateInterval < CITY_PROCESS_INTERVAL)
|
|
cityUpdateInterval = CITY_UPDATE_INTERVAL;
|
|
|
|
int currentTime = getGameTime();
|
|
int nextTime = currentTime + cityUpdateInterval;
|
|
|
|
if(cityUpdateIds != null && cityUpdateTimes != null && cityUpdateIds.length == cityUpdateTimes.length)
|
|
{
|
|
// Process any pending city updates.
|
|
for(int i = 0; i < cityUpdateIds.length; ++i)
|
|
{
|
|
if(force || cityUpdateTimes[i] <= currentTime)
|
|
{
|
|
cityUpdateTimes[i] = nextTime;
|
|
cityUpdate(cityUpdateIds[i], self, cityUpdateInterval, true);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cityUpdateIds = null;
|
|
cityUpdateTimes = null;
|
|
}
|
|
|
|
// Update the city information to match all current cities.
|
|
int[] cityIds = getAllCityIds();
|
|
int[] newCityUpdateTimes = new int[cityIds.length];
|
|
|
|
for(int i = 0; i < cityIds.length; ++i)
|
|
{
|
|
int pos = findIntOffsetInTable(cityUpdateIds, cityIds[i]);
|
|
|
|
if(pos == -1)
|
|
newCityUpdateTimes[i] = nextTime;
|
|
else
|
|
newCityUpdateTimes[i] = cityUpdateTimes[pos];
|
|
}
|
|
|
|
objvar_mangle.setMangledIntArrayObjVar(self, "cityUpdate.ids", cityIds);
|
|
objvar_mangle.setMangledIntArrayObjVar(self, "cityUpdate.times", newCityUpdateTimes);
|
|
}
|
|
|
|
//------------------------------------------------
|
|
// forceUpdate
|
|
//------------------------------------------------
|
|
|
|
messageHandler forceUpdate()
|
|
{
|
|
// A god mode player has forced a city update.
|
|
int city_id = params.getInt("city_id");
|
|
int no_citizen_cleanup = params.getInt("no_citizen_cleanup");
|
|
|
|
cityUpdate(city_id, self, CITY_UPDATE_INTERVAL, (no_citizen_cleanup == 0));
|
|
|
|
return SCRIPT_CONTINUE;
|
|
}
|
|
|
|
//------------------------------------------------
|
|
// forceElection
|
|
//------------------------------------------------
|
|
|
|
messageHandler forceElection()
|
|
{
|
|
// A god mode player has forced a city election.
|
|
int city_id = params.getInt("city_id");
|
|
|
|
electMayor(city_id, self, CITY_UPDATE_INTERVAL);
|
|
|
|
return SCRIPT_CONTINUE;
|
|
}
|
|
|
|
//------------------------------------------------
|
|
// forceRank
|
|
//------------------------------------------------
|
|
|
|
messageHandler forceRank()
|
|
{
|
|
// A god mode player has forced a city rank change.
|
|
int city_id = params.getInt("city_id");
|
|
int rank_change = params.getInt("rank_change");
|
|
int rank = city.getCityRank(city_id) - 1;
|
|
|
|
if(rank_change > 0)
|
|
{
|
|
increaseRank(city_id, rank, true);
|
|
}
|
|
else if(rank_change < 0)
|
|
{
|
|
decreaseRank(city_id, rank);
|
|
}
|
|
|
|
return SCRIPT_CONTINUE;
|
|
}
|
|
|
|
//------------------------------------------------
|
|
// cityUpdate
|
|
//------------------------------------------------
|
|
|
|
void cityUpdate(int city_id, obj_id self, int interval, boolean cleanup)
|
|
{
|
|
// Do this city's weekly update.
|
|
obj_id cityHall = cityGetCityHall(city_id);
|
|
string name = cityGetName(city_id);
|
|
|
|
CustomerServiceLog("player_city", "Updating City: " + name + " (" + city_id + "/" + cityHall + ")");
|
|
|
|
// Tally mayoral votes and elect a new mayor.
|
|
int cityVoteInterval = getIntObjVar(self, "vote_interval." + city_id);
|
|
|
|
cityVoteInterval++;
|
|
|
|
if(cityVoteInterval == 3)
|
|
{
|
|
// Elect a new mayor every 3rd week.
|
|
electMayor(city_id, self, interval);
|
|
cityVoteInterval = 0;
|
|
}
|
|
|
|
setObjVar(self, "vote_interval." + city_id, cityVoteInterval);
|
|
dictionary outparams = new dictionary();
|
|
|
|
outparams.put("cityVoteInterval", cityVoteInterval);
|
|
if(!messageTo(cityHall, "setVoteInterval", outparams, 0.f, true))
|
|
{
|
|
//vote interval not updated - failed messageTo
|
|
CustomerServiceLog("player_city", "Updating City: Vote Interval not Updated - Should have been set to "+ cityVoteInterval +". City: " + name + " (" + city_id + "/" + cityHall + ")");
|
|
}
|
|
|
|
// Grant the mayor an xp bonus, since we want them to gain skills a bit faster than the 3 week voting cycle.
|
|
obj_id incumbent_mayor = cityGetLeader(city_id);
|
|
|
|
grantExperiencePoints(incumbent_mayor, xp.POLITICAL, 750);
|
|
|
|
// Check city radius.
|
|
validateCityRadius(city_id, self);
|
|
|
|
// Check civic structure cap.
|
|
checkCivicCap(city_id, self);
|
|
|
|
// Collect taxes.
|
|
collectTaxes(city_id, self);
|
|
|
|
// Perform city maintenance.
|
|
cityMaintenance(city_id, self);
|
|
|
|
// Clean up city intervals.
|
|
obj_var_list ovl = getObjVarList(self, "vote_interval");
|
|
|
|
if(ovl != null)
|
|
{
|
|
for(int i = 0; i < ovl.getNumItems(); i++)
|
|
{
|
|
obj_var ov = ovl.getObjVar(i);
|
|
string test_name = ov.getName();
|
|
|
|
int test_id = utils.stringToInt(test_name);
|
|
|
|
if(!cityExists(test_id))
|
|
{
|
|
removeObjVar(self, "vote_interval." + test_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clean up the citizen list.
|
|
/**** Removed until we figure out why citizens are being removed out of turn.
|
|
if ( cleanup )
|
|
cleanupCitizens( city_id, self );
|
|
****/
|
|
}
|
|
|
|
//------------------------------------------------
|
|
// electMayor
|
|
//------------------------------------------------
|
|
|
|
void electMayor(int city_id, obj_id self, int interval)
|
|
{
|
|
// Do this city's weekly election.
|
|
obj_id cityHall = cityGetCityHall(city_id);
|
|
string city_name = cityGetName(city_id);
|
|
|
|
CustomerServiceLog("player_city", "Election Cycle: " + city_name + " (" + city_id + "/" + cityHall + ")");
|
|
|
|
// Accumulate a list of who everyone is voting for.
|
|
resizeable obj_id[] vote_ids = new obj_id[0];
|
|
resizeable int[] vote_counts = new int[0];
|
|
|
|
obj_id[] citizens = cityGetCitizenIds(city_id);
|
|
|
|
if(citizens.length <= 0)
|
|
{
|
|
CustomerServiceLog("player_city", "Election Cycle for City: " + city_name + " (" + city_id + "/" + cityHall + ") INVALID Citizen Array - Allowing to continue momentarily");
|
|
}
|
|
|
|
for(int i = 0; i < citizens.length; i++)
|
|
{
|
|
if(!isIdValid(citizens[i]))
|
|
CustomerServiceLog("player_city", "Election Cycle for City: " + city_name + " (" + city_id + "/" + cityHall + "). Citizen: (" + citizens[i] + ") is Invalid.");
|
|
|
|
boolean found = false;
|
|
|
|
obj_id vote = cityGetCitizenAllegiance(city_id, citizens[i]);
|
|
|
|
//make sure the 'Vote' is actually a Citizen of the City at the
|
|
//time the elections calculate a winner.
|
|
if(isIdValid(vote) && city.isCitizenOfCity(vote, city_id))
|
|
{
|
|
for(int j = 0; (j < vote_ids.length); j++)
|
|
{
|
|
// Increment vote count for a person who has been voted for.
|
|
if(vote_ids[j] == vote)
|
|
{
|
|
vote_counts[j] = vote_counts[j] + 1;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!found)
|
|
{
|
|
// Person has not been voted for yet.
|
|
utils.addElement(vote_ids, vote);
|
|
utils.addElement(vote_counts, 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CustomerServiceLog("player_city", "Election Cycle for City: " + city_name + " (" + city_id + "/" + cityHall + "). Citizen: (" + citizens[i] + ") has 'Voted for/allegience to' an Invalid ID(" + vote + ") This vote won't count.");
|
|
}
|
|
}
|
|
|
|
// Figure out who earned the most votes.
|
|
obj_id incumbent_mayor = cityGetLeader(city_id);
|
|
obj_id new_mayor = null;
|
|
int max_votes = 0;
|
|
int incumbent_votes = 0;
|
|
int xp_granted = 0;
|
|
int csTotal = 0;
|
|
|
|
//no one voted - CS Log because a '***Null***' log will hit later
|
|
if(vote_ids.length == 0)
|
|
{
|
|
CustomerServiceLog("player_city", "Election Cycle: No one Voted in this election. City: " + city_name + " (" + city_id + "/" + cityHall + "). Incumbent will remain Mayor - ***NULL*** CS Log will be generated below.");
|
|
}
|
|
|
|
for(int i = 0; i < vote_ids.length; i++)
|
|
{
|
|
// Grant xp.
|
|
// Add obj_id validation to fix granting XP to no target bug
|
|
// We may in the future want to change this to a messageTo to verify that the
|
|
// receipient always gets his or her XP, even if they are offline
|
|
if(isIdValid(vote_ids[i]))
|
|
{
|
|
CustomerServiceLog("player_city", "Election Cycle: Player has recieved " + vote_counts[i] + " votes granting " + vote_counts[i] * 100 + " points of " + xp.POLITICAL + " XP for running in the " + city_name + " (" + city_id + "/" + cityHall + ") election. " + vote_ids[i]);
|
|
grantExperiencePoints(vote_ids[i], xp.POLITICAL, vote_counts[i] * 300);
|
|
xp_granted++;
|
|
}
|
|
else
|
|
{
|
|
CustomerServiceLog("player_city", "Election Cycle: Invalid ID ("+ vote_ids[i] +") has recieved " + vote_counts[i] + " votes in the City Election: " + city_name + " (" + city_id + "/" + cityHall + "). No Experience was granted due to Invalid ID.");
|
|
}
|
|
|
|
if(vote_ids[i] == incumbent_mayor)
|
|
incumbent_votes = vote_counts[i];
|
|
|
|
if(vote_counts[i] > max_votes)
|
|
{
|
|
max_votes = vote_counts[i];
|
|
new_mayor = vote_ids[i];
|
|
}
|
|
|
|
csTotal += vote_counts[i];
|
|
}
|
|
|
|
// if ( (vote_ids.length > 0) && (citizens.length != csTotal) )
|
|
// CustomerServiceLog("player_city", "XP Error for " + city_name + "("+ city_id + "/" + cityHall + "). There were " + citizens.length + " possible votes but only " + csTotal + " votes were counted for XP");
|
|
|
|
// If no one got any votes, retain the incumbent.
|
|
if(new_mayor == null)
|
|
{
|
|
CustomerServiceLog("player_city_transfer", "Incumbent Mayor ("+ incumbent_mayor +")"+ cityGetCitizenName(city_id, incumbent_mayor) +" Of City("+ city_id +")"+ cityGetName(city_id) +" Wins due to NEW MAYOR obj_id being - **** NULL **** - ");
|
|
new_mayor = incumbent_mayor;
|
|
}
|
|
boolean incumbent_win = false;
|
|
|
|
// Check for an incumbent win.
|
|
// Note that the incumbent wins all ties. Other ties are settled by whoever happens to be first in the list.
|
|
// The incumbent wins if the winner is already a mayor of another city.
|
|
if((new_mayor == incumbent_mayor) || (new_mayor == null) || (max_votes == incumbent_votes) || city.isAMayor(new_mayor))
|
|
{
|
|
// Keep the incumbent.
|
|
CustomerServiceLog("player_city", "Incumbent Win: " + incumbent_mayor + " Votes: " + incumbent_votes + " City: " + city_name + " (" + city_id + "/" + cityHall + ")");
|
|
|
|
// Notify the incumbent.
|
|
string imayor_name = cityGetCitizenName(city_id, incumbent_mayor);
|
|
prose_package bodypp = prose.getPackage(ELECTION_INCUMBENT_WIN_BODY, incumbent_mayor, city_name);
|
|
|
|
utils.sendMail(ELECTION_INCUMBENT_WIN_SUBJECT, bodypp, imayor_name, "City Hall");
|
|
|
|
incumbent_win = true;
|
|
}
|
|
else
|
|
{
|
|
CustomerServiceLog("player_city_transfer", "New Mayor: " + new_mayor + " Votes: " + max_votes + " City: " + city_name + " (" + city_id + "/" + cityHall + ")");
|
|
|
|
// Elect a new mayor.
|
|
city.setMayor( city_id, new_mayor );
|
|
|
|
// Notify the new mayor.
|
|
string nmayor_name = cityGetCitizenName(city_id, new_mayor);
|
|
prose_package bodypp = prose.getPackage(ELECTION_NEW_MAYOR_BODY, new_mayor, city_name);
|
|
|
|
utils.sendMail(ELECTION_NEW_MAYOR_SUBJECT, bodypp, nmayor_name, "City Hall");
|
|
|
|
// Notify the old mayor.
|
|
string imayor_name = cityGetCitizenName(city_id, incumbent_mayor);
|
|
|
|
bodypp = prose.getPackage(ELECTION_INCUMBENT_LOST_BODY, new_mayor, city_name);
|
|
utils.sendMail(ELECTION_INCUMBENT_LOST_SUBJECT, bodypp, imayor_name, "City Hall");
|
|
|
|
// Notify all structures of the new mayor.
|
|
// Civic structures will use this message to change their owner and admin references.
|
|
city.doMayoralStructureTransfer(new_mayor, incumbent_mayor, city_id);
|
|
}
|
|
|
|
// Reset all allegiance to null.
|
|
for(int i = 0; i < citizens.length; i++)
|
|
{
|
|
city.setCitizenAllegiance(city_id, citizens[i], null);
|
|
}
|
|
|
|
// Tell the city hall to wipe voting records.
|
|
dictionary outparams = new dictionary();
|
|
|
|
outparams.put("lastUpdateTime", getGameTime());
|
|
outparams.put("currentInterval", interval);
|
|
|
|
if(!messageTo(cityGetCityHall(city_id), "resetVoteTerminal", outparams, 0.f, true))
|
|
{
|
|
CustomerServiceLog("player_city_transfer", "ResetVotingTerminal for City ("+ city_id +") "+ cityGetName(city_id) +" FAILED - MessageTo Failed to send: Candidate List has not been removed properly.");
|
|
}
|
|
|
|
// Notify the public.
|
|
string nmayor_name = cityGetCitizenName(city_id, new_mayor);
|
|
|
|
for(int i = 0; i < citizens.length; i++)
|
|
{
|
|
string citizen_name = cityGetCitizenName(city_id, citizens[i]);
|
|
|
|
if(incumbent_win)
|
|
{
|
|
prose_package bodypp = prose.getPackage(PUBLIC_ELECTION_INC_BODY, city_name, nmayor_name);
|
|
|
|
utils.sendMail(PUBLIC_ELECTION_INC_SUBJECT, bodypp, citizen_name, "City Hall");
|
|
}
|
|
else
|
|
{
|
|
prose_package bodypp = prose.getPackage(PUBLIC_ELECTION_BODY, city_name, nmayor_name);
|
|
|
|
utils.sendMail(PUBLIC_ELECTION_SUBJECT, bodypp, citizen_name, "City Hall");
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------
|
|
// validateCityRadius
|
|
//------------------------------------------------
|
|
|
|
void validateCityRadius(int city_id, obj_id self)
|
|
{
|
|
obj_id cityHall = cityGetCityHall(city_id);
|
|
string city_name = cityGetName(city_id);
|
|
|
|
CustomerServiceLog("player_city", "City Validation: " + city_name + " (" + city_id + "/" + cityHall + ")");
|
|
|
|
int[] radiusList = dataTableGetIntColumn(city.RANK_TABLE, city.RANK_RADIUS);
|
|
int[] popList = dataTableGetIntColumn(city.RANK_TABLE, city.RANK_POPULATION);
|
|
|
|
obj_id mayor = cityGetLeader(city_id);
|
|
string mayor_name = cityGetCitizenName(city_id, mayor);
|
|
|
|
// Get our current radius and population requirement.
|
|
int currentRadius = cityGetRadius(city_id);
|
|
int currentPopReq = 0;
|
|
int nextPopReq = 0;
|
|
int rank = 0;
|
|
|
|
for(int i = 0; i < popList.length; i++)
|
|
{
|
|
if(radiusList[i] == currentRadius)
|
|
{
|
|
rank = i;
|
|
currentPopReq = popList[i];
|
|
if(i + 1 < popList.length)
|
|
nextPopReq = popList[i + 1];
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Get our current population.
|
|
obj_id[] citizens = cityGetCitizenIds(city_id);
|
|
|
|
// Check our status.
|
|
if(citizens.length < popList[0])
|
|
{
|
|
// We've fallen below the min citizen count to be valid.
|
|
rank = 0;
|
|
int newRadius = radiusList[rank];
|
|
dictionary params = new dictionary();
|
|
|
|
params.put("rank", rank);
|
|
params.put("radius", newRadius);
|
|
messageTo(cityHall, "contractCity", params, 0.f, true);
|
|
|
|
// Notify the mayor.
|
|
prose_package bodypp = prose.getPackage(CITY_INVALID_BODY, city_name, rank + 1);
|
|
|
|
utils.sendMail(CITY_INVALID_SUBJECT, bodypp, mayor_name, "City Hall");
|
|
|
|
// Force the city to revalidate.
|
|
messageTo(cityHall, "validateCity", null, 259200.f, true);
|
|
|
|
CustomerServiceLog("player_city", "City Below Min Citizen Requirement! Population: " + citizens.length + " City: " + city_name + " (" + city_id + "/" + cityHall + ")");
|
|
}
|
|
else if(citizens.length < currentPopReq)
|
|
{
|
|
// Our city has decreased in size.
|
|
// Godhacked cities with this objvar are exempt from deranking.
|
|
if(!hasObjVar(cityHall, city.OBJVAR_DERANK_EXEMPT))
|
|
decreaseRank(city_id, rank);
|
|
}
|
|
else if((nextPopReq > 0) && (citizens.length >= nextPopReq))
|
|
{
|
|
// Our city has increased in size.
|
|
increaseRank(city_id, rank);
|
|
}
|
|
else
|
|
{
|
|
CustomerServiceLog("player_city", "City: " + city_name + " (" + city_id + "/" + cityHall + ")" + " Rank: " + rank + " CITY POP: " + citizens.length + " POP REQ: " + currentPopReq + " NEXT REQ: " + nextPopReq);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------
|
|
// decreaseRank
|
|
//------------------------------------------------
|
|
|
|
void decreaseRank(int city_id, int rank)
|
|
{
|
|
obj_id cityHall = cityGetCityHall(city_id);
|
|
|
|
// Shouldn't hypothetically happen but a secondary check to prevent deranking of exempt cities.
|
|
if(hasObjVar(cityHall, city.OBJVAR_DERANK_EXEMPT))
|
|
return;
|
|
|
|
int[] radiusList = dataTableGetIntColumn(city.RANK_TABLE, city.RANK_RADIUS);
|
|
int[] popList = dataTableGetIntColumn(city.RANK_TABLE, city.RANK_POPULATION);
|
|
|
|
obj_id mayor = cityGetLeader(city_id);
|
|
string mayor_name = cityGetCitizenName(city_id, mayor);
|
|
string city_name = cityGetName(city_id);
|
|
|
|
rank--;
|
|
|
|
if(rank < 0)
|
|
rank = 0;
|
|
|
|
int newRadius = radiusList[rank];
|
|
|
|
CustomerServiceLog("player_city", "The city shrank. Rank: " + rank + " City: " + city_name + " (" + city_id + "/" + cityHall + ")");
|
|
|
|
// Tell the city hall to remove structures outside our new radius.
|
|
// Sent non-persistent as any structure loaded after will automatically remove itself from the city.
|
|
dictionary params = new dictionary();
|
|
|
|
params.put("rank", rank);
|
|
params.put("radius", newRadius);
|
|
messageTo(cityHall, "contractCity", params, 0.f, true);
|
|
|
|
// Notify the mayor.
|
|
prose_package bodypp = prose.getPackage(CITY_CONTRACT_BODY, city_name, rank + 1);
|
|
|
|
utils.sendMail(CITY_CONTRACT_SUBJECT, bodypp, mayor_name, "City Hall");
|
|
}
|
|
|
|
//------------------------------------------------
|
|
// increaseRank
|
|
//------------------------------------------------
|
|
|
|
void increaseRank(int city_id, int rank, boolean forced)
|
|
{
|
|
int[] radiusList = dataTableGetIntColumn(city.RANK_TABLE, city.RANK_RADIUS);
|
|
int[] popList = dataTableGetIntColumn(city.RANK_TABLE, city.RANK_POPULATION);
|
|
|
|
obj_id cityHall = cityGetCityHall(city_id);
|
|
obj_id mayor = cityGetLeader(city_id);
|
|
string mayor_name = cityGetCitizenName(city_id, mayor);
|
|
string city_name = cityGetName(city_id);
|
|
location our_loc = cityGetLocation(city_id);
|
|
string planet_name = our_loc.area;
|
|
|
|
rank++;
|
|
|
|
if(rank > city.RANK_MAX)
|
|
rank = city.RANK_MAX;
|
|
|
|
int newRadius = radiusList[rank];
|
|
|
|
int medium_city_limit = 0;
|
|
int big_city_limit = 0;
|
|
|
|
string[] planets = dataTableGetStringColumn("datatables/city/city_limits.iff", "SCENE");
|
|
|
|
for(int i = 0; i < planets.length; i++)
|
|
{
|
|
if(planets[i].equals(planet_name))
|
|
{
|
|
medium_city_limit = dataTableGetInt("datatables/city/city_limits.iff", i, "MEDIUM_CITY_LIMIT");
|
|
big_city_limit = dataTableGetInt("datatables/city/city_limits.iff", i, "BIG_CITY_LIMIT");
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Check to see if we can increase rank.
|
|
if(rank == 2) // (rank 3, rank is zero based in this function)
|
|
{
|
|
// Are there X or more cities rank 3 or larger?
|
|
int mid_size_cities = 0;
|
|
int[] city_ids = getAllCityIds();
|
|
|
|
for(int i = 0; i < city_ids.length; i++)
|
|
{
|
|
location city_loc = cityGetLocation(city_ids[i]);
|
|
|
|
if(city_loc.area.equals(planet_name) && (city.getCityRank(city_ids[i]) >= 3))
|
|
mid_size_cities++;
|
|
}
|
|
|
|
if(!forced)
|
|
{
|
|
|
|
if(mid_size_cities >= medium_city_limit)
|
|
{
|
|
// We can't increase in size.
|
|
prose_package bodypp = prose.getPackage(CITY_EXPAND_CAP_BODY, city_name, rank + 1);
|
|
|
|
utils.sendMail(CITY_EXPAND_CAP_SUBJECT, bodypp, mayor_name, "Planetary Civic Authority");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else if(rank == 3) // rank 4 (if we are becoming rank 5, its ok)
|
|
{
|
|
// Are there X or more cities rank 4 or 5?
|
|
int large_size_cities = 0;
|
|
int[] city_ids = getAllCityIds();
|
|
|
|
for(int i = 0; i < city_ids.length; i++)
|
|
{
|
|
location city_loc = cityGetLocation(city_ids[i]);
|
|
|
|
if(city_loc.area.equals(planet_name) && (city.getCityRank(city_ids[i]) >= 4))
|
|
large_size_cities++;
|
|
}
|
|
|
|
if(!forced)
|
|
{
|
|
if(large_size_cities >= big_city_limit)
|
|
{
|
|
// We can't increase in size.
|
|
prose_package bodypp = prose.getPackage(CITY_EXPAND_CAP_BODY, city_name, rank + 1);
|
|
|
|
utils.sendMail(CITY_EXPAND_CAP_SUBJECT, bodypp, mayor_name, "Planetary Civic Authority");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
CustomerServiceLog("player_city", "The city grew. Rank: " + rank + " City: " + city_name + " (" + city_id + "/" + cityHall + ")");
|
|
|
|
// Tell the city hall to add new structures within our new radius.
|
|
// Sent non-persistent as any structure loaded after will automatically add itself to the city.
|
|
dictionary params = new dictionary();
|
|
|
|
params.put("rank", rank);
|
|
params.put("radius", newRadius);
|
|
messageTo(cityHall, "expandCity", params, 0.f, true);
|
|
|
|
// Notify the mayor.
|
|
prose_package bodypp = prose.getPackage(CITY_EXPAND_BODY, city_name, rank + 1);
|
|
|
|
utils.sendMail(CITY_EXPAND_SUBJECT, bodypp, mayor_name, "City Hall");
|
|
}
|
|
|
|
void increaseRank(int city_id, int rank)
|
|
{
|
|
increaseRank(city_id, rank, false);
|
|
}
|
|
|
|
//------------------------------------------------
|
|
// collectTaxes
|
|
//------------------------------------------------
|
|
|
|
void collectTaxes(int city_id, obj_id self)
|
|
{
|
|
// Property tax is handled by the maintenance system.
|
|
// Travel tax is handled by the travel system.
|
|
// Sales tax is handled by the commodities system.
|
|
}
|
|
|
|
//------------------------------------------------
|
|
// cityMaintenance
|
|
//------------------------------------------------
|
|
|
|
void cityMaintenance(int city_id, obj_id self)
|
|
{
|
|
obj_id city_hall = cityGetCityHall(city_id);
|
|
string city_name = cityGetName(city_id);
|
|
|
|
CustomerServiceLog("player_city", "City Maintenance: " + city_name + " (" + city_id + "/" + city_hall + ")");
|
|
|
|
// Tell the city hall to pay maintenance.
|
|
messageTo(city_hall, "payMaintenance", null, 0.f, true);
|
|
}
|
|
|
|
//------------------------------------------------
|
|
// findIntOffsetInTable
|
|
//------------------------------------------------
|
|
|
|
int findIntOffsetInTable(int[] from, int find)
|
|
{
|
|
if(from != null)
|
|
for(int i = 0; i < from.length; ++i)
|
|
if(from[i] == find)
|
|
return i;
|
|
return -1;
|
|
}
|
|
|
|
//------------------------------------------------
|
|
// confirmCityRemoved
|
|
//------------------------------------------------
|
|
|
|
messageHandler confirmCityRemoved()
|
|
{
|
|
obj_id city_hall = params.getObjId("city_hall");
|
|
|
|
if(city_hall == null)
|
|
return SCRIPT_CONTINUE;
|
|
|
|
int city_id = findCityByCityHall(city_hall);
|
|
|
|
if(cityExists(city_id))
|
|
{
|
|
string city_name = cityGetName(city_id);
|
|
|
|
debug.debugAllMsg("PLAYER CITY", self, "A city continued to exist after its hall was deleted! Attempting to remove the city. City: " + city_id + " Hall: " + city_hall);
|
|
CustomerServiceLog("player_city", "A city continued to exist after its hall was deleted! Attempting to remove the city. City: " + city_name + " (" + city_id + "/" + city_hall + ")");
|
|
removeCity(city_id);
|
|
|
|
return SCRIPT_CONTINUE;
|
|
}
|
|
|
|
debug.debugAllMsg("PLAYER CITY", self, "City master object confirmed removal of city. City: " + city_id + " Hall: " + city_hall);
|
|
CustomerServiceLog("player_city", "City master object confirmed removal of city. City: " + " (" + city_id + "/" + city_hall + ")");
|
|
|
|
return SCRIPT_CONTINUE;
|
|
}
|
|
|
|
//------------------------------------------------
|
|
// reportUpdateEstimate
|
|
//------------------------------------------------
|
|
|
|
messageHandler reportUpdateEstimate()
|
|
{
|
|
obj_id player = params.getObjId("player");
|
|
int city_id = params.getInt("city_id");
|
|
|
|
int[] cityUpdateIds = objvar_mangle.getMangledIntArrayObjVar(self, "cityUpdate.ids");
|
|
int[] cityUpdateTimes = objvar_mangle.getMangledIntArrayObjVar(self, "cityUpdate.times");
|
|
|
|
if(cityUpdateIds != null && cityUpdateTimes != null && cityUpdateIds.length == cityUpdateTimes.length)
|
|
{
|
|
// Process any pending city updates.
|
|
for(int i = 0; i < cityUpdateIds.length; ++i)
|
|
{
|
|
if(cityUpdateIds[i] == city_id)
|
|
{
|
|
int curt = getGameTime();
|
|
int diff = cityUpdateTimes[i] - curt;
|
|
|
|
string time_remaining = player_structure.assembleTimeRemaining(player_structure.convertSecondsTime(diff));
|
|
|
|
if(time_remaining.equals("error"))
|
|
time_remaining = "Update Pending: Your city will update with the next server city pulse.";
|
|
|
|
dictionary outparams = new dictionary();
|
|
|
|
outparams.put("time_remaining", time_remaining);
|
|
messageTo(player, "displayCityUpdateTime", outparams, 0.f, false);
|
|
|
|
return SCRIPT_CONTINUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return SCRIPT_CONTINUE;
|
|
}
|
|
|
|
//------------------------------------------------
|
|
// checkCivicCap
|
|
//------------------------------------------------
|
|
|
|
void checkCivicCap(int city_id, obj_id self)
|
|
{
|
|
// Tell the city hall to pay maintenance.
|
|
obj_id city_hall = cityGetCityHall(city_id);
|
|
|
|
messageTo(city_hall, "checkCivicCap", null, 0.f, true);
|
|
}
|
|
|
|
//------------------------------------------------
|
|
// cleanupCitizens
|
|
//------------------------------------------------
|
|
|
|
void cleanupCitizens(int city_id, obj_id self)
|
|
{
|
|
// Get our current population.
|
|
obj_id mayor = cityGetLeader(city_id);
|
|
|
|
obj_id[] citizens = cityGetCitizenIds(city_id);
|
|
|
|
for(int i = 0; i < citizens.length; i++)
|
|
{
|
|
if(citizens[i] == mayor)
|
|
continue;
|
|
|
|
int flags = cityGetCitizenPermissions(city_id, citizens[i]);
|
|
|
|
if((flags & city.CP_ABSENT_WEEK_5) != 0)
|
|
{
|
|
// This person has been gone a while, remove them from the citizen list.
|
|
string mayor_name = cityGetCitizenName(city_id, mayor);
|
|
prose_package bodypp = prose.getPackage(city.LOST_INACTIVE_CITIZEN_BODY, cityGetCitizenName(city_id, citizens[i]));
|
|
|
|
utils.sendMail(city.LOST_INACTIVE_CITIZEN_SUBJECT, bodypp, mayor_name, "City Hall");
|
|
|
|
string city_name = cityGetName(city_id);
|
|
obj_id city_hall = cityGetCityHall(city_id);
|
|
|
|
cityRemoveCitizen(city_id, citizens[i]);
|
|
CustomerServiceLog("player_city", "Removed citizen due to inactivity. City: " + city_name + " (" + city_id + "/" + city_hall + ")" + " Citizen: " + citizens[i]);
|
|
continue;
|
|
}
|
|
else if((flags & city.CP_ABSENT_WEEK_4) != 0)
|
|
flags = flags | city.CP_ABSENT_WEEK_5;
|
|
else if((flags & city.CP_ABSENT_WEEK_3) != 0)
|
|
flags = flags | city.CP_ABSENT_WEEK_4;
|
|
else if((flags & city.CP_ABSENT_WEEK_2) != 0)
|
|
flags = flags | city.CP_ABSENT_WEEK_3;
|
|
else if((flags & city.CP_ABSENT_WEEK_1) != 0)
|
|
flags = flags | city.CP_ABSENT_WEEK_2;
|
|
else
|
|
flags = flags | city.CP_ABSENT_WEEK_1;
|
|
|
|
string citname = cityGetCitizenName(city_id, citizens[i]);
|
|
obj_id citall = cityGetCitizenAllegiance(city_id, citizens[i]);
|
|
|
|
citySetCitizenInfo(city_id, citizens[i], citname, citall, flags);
|
|
}
|
|
}
|