// ====================================================================== // // 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); } }