
ch.sahits.game.openpatrician.engine.land.city.CityHallEngine Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of OpenPatricianEngine Show documentation
Show all versions of OpenPatricianEngine Show documentation
Engine part driving the models and the game
package ch.sahits.game.openpatrician.engine.land.city;
import ch.sahits.game.event.ShipAttackEvent;
import ch.sahits.game.event.data.NewGameClient;
import ch.sahits.game.event.data.PeriodicalTimeDayUpdate;
import ch.sahits.game.event.data.PeriodicalTimeMonthEndUpdate;
import ch.sahits.game.event.data.PeriodicalTimeWeekEndUpdate;
import ch.sahits.game.event.data.PeriodicalTimeYearEndUpdate;
import ch.sahits.game.openpatrician.annotation.ClassCategory;
import ch.sahits.game.openpatrician.annotation.EClassCategory;
import ch.sahits.game.openpatrician.annotation.LazySingleton;
import ch.sahits.game.openpatrician.collections.SortedMapRandomizedSameElements;
import ch.sahits.game.openpatrician.engine.AbstractEngine;
import ch.sahits.game.openpatrician.engine.EngineFactory;
import ch.sahits.game.openpatrician.engine.land.city.internal.CityWallMaterialBuyingTask;
import ch.sahits.game.openpatrician.engine.land.city.internal.ElectionTask;
import ch.sahits.game.openpatrician.engine.land.city.internal.VoteTask;
import ch.sahits.game.openpatrician.model.Date;
import ch.sahits.game.openpatrician.model.DisplayMessage;
import ch.sahits.game.openpatrician.model.IAIPlayer;
import ch.sahits.game.openpatrician.model.ICitizen;
import ch.sahits.game.openpatrician.model.ICompany;
import ch.sahits.game.openpatrician.model.IMap;
import ch.sahits.game.openpatrician.model.IPlayer;
import ch.sahits.game.openpatrician.model.building.IBarn;
import ch.sahits.game.openpatrician.model.building.IBuilding;
import ch.sahits.game.openpatrician.model.building.ICityWall;
import ch.sahits.game.openpatrician.model.building.IMarketplace;
import ch.sahits.game.openpatrician.model.building.ITradingOffice;
import ch.sahits.game.openpatrician.model.city.ECityWall;
import ch.sahits.game.openpatrician.model.city.EPopulationClass;
import ch.sahits.game.openpatrician.model.city.ICity;
import ch.sahits.game.openpatrician.model.city.ICreditor;
import ch.sahits.game.openpatrician.model.city.ILoaner;
import ch.sahits.game.openpatrician.model.city.cityhall.CityHallList;
import ch.sahits.game.openpatrician.model.city.cityhall.ECityViolationPunishment;
import ch.sahits.game.openpatrician.model.city.cityhall.EldermanCandidateList;
import ch.sahits.game.openpatrician.model.city.cityhall.EldermanTaskPlayerMap;
import ch.sahits.game.openpatrician.model.city.cityhall.IAcceptedEldermanTask;
import ch.sahits.game.openpatrician.model.city.cityhall.IBowmen;
import ch.sahits.game.openpatrician.model.city.cityhall.IBuildLandPassage;
import ch.sahits.game.openpatrician.model.city.cityhall.ICapturePirateNest;
import ch.sahits.game.openpatrician.model.city.cityhall.ICityGuard;
import ch.sahits.game.openpatrician.model.city.cityhall.ICityHall;
import ch.sahits.game.openpatrician.model.city.cityhall.ICityHallNotice;
import ch.sahits.game.openpatrician.model.city.cityhall.ICityPetition;
import ch.sahits.game.openpatrician.model.city.cityhall.ICityViolation;
import ch.sahits.game.openpatrician.model.city.cityhall.ICityWallPetition;
import ch.sahits.game.openpatrician.model.city.cityhall.ICrossbowmen;
import ch.sahits.game.openpatrician.model.city.cityhall.ICustomsViolation;
import ch.sahits.game.openpatrician.model.city.cityhall.IEldermanOffice;
import ch.sahits.game.openpatrician.model.city.cityhall.IEldermanTask;
import ch.sahits.game.openpatrician.model.city.cityhall.IFoundNewSettlement;
import ch.sahits.game.openpatrician.model.city.cityhall.IHeadTaxPetition;
import ch.sahits.game.openpatrician.model.city.cityhall.IHelpCity;
import ch.sahits.game.openpatrician.model.city.cityhall.IHuntPirate;
import ch.sahits.game.openpatrician.model.city.cityhall.IMilitiaPetition;
import ch.sahits.game.openpatrician.model.city.cityhall.IMusketeer;
import ch.sahits.game.openpatrician.model.city.cityhall.IOutriggerContract;
import ch.sahits.game.openpatrician.model.city.cityhall.IPikemen;
import ch.sahits.game.openpatrician.model.city.cityhall.IPirateSupportViolation;
import ch.sahits.game.openpatrician.model.city.cityhall.IPlunderTradingOfficesViolation;
import ch.sahits.game.openpatrician.model.city.cityhall.ISpecialTaxPetition;
import ch.sahits.game.openpatrician.model.city.cityhall.ISpecialTaxViolation;
import ch.sahits.game.openpatrician.model.city.cityhall.ITreasury;
import ch.sahits.game.openpatrician.model.city.cityhall.impl.Ballot;
import ch.sahits.game.openpatrician.model.city.cityhall.impl.CityHall;
import ch.sahits.game.openpatrician.model.city.cityhall.impl.CityViolation;
import ch.sahits.game.openpatrician.model.city.cityhall.impl.CustomsViolation;
import ch.sahits.game.openpatrician.model.city.cityhall.impl.EldermanOffice;
import ch.sahits.game.openpatrician.model.city.cityhall.impl.Election;
import ch.sahits.game.openpatrician.model.city.cityhall.impl.PlunderTradingOfficeViolation;
import ch.sahits.game.openpatrician.model.city.cityhall.impl.SpecialTaxPetition;
import ch.sahits.game.openpatrician.model.city.cityhall.impl.SpecialTaxViolation;
import ch.sahits.game.openpatrician.model.city.cityhall.impl.Treasury;
import ch.sahits.game.openpatrician.model.city.impl.CityWall;
import ch.sahits.game.openpatrician.model.city.impl.Debt;
import ch.sahits.game.openpatrician.model.event.TimedUpdatableTaskList;
import ch.sahits.game.openpatrician.model.factory.StateFactory;
import ch.sahits.game.openpatrician.model.impl.BalanceSheet;
import ch.sahits.game.openpatrician.model.people.ISeaPirate;
import ch.sahits.game.openpatrician.model.people.PeopleFactory;
import ch.sahits.game.openpatrician.model.people.impl.SeaPiratesState;
import ch.sahits.game.openpatrician.model.product.AmountablePrice;
import ch.sahits.game.openpatrician.model.product.EWare;
import ch.sahits.game.openpatrician.model.product.IWare;
import ch.sahits.game.openpatrician.model.sea.PirateNest;
import ch.sahits.game.openpatrician.model.util.CityUtilities;
import ch.sahits.game.openpatrician.util.l10n.Locale;
import com.google.common.annotations.VisibleForTesting;
import java.util.Optional;
import com.google.common.eventbus.AsyncEventBus;
import com.google.common.eventbus.Subscribe;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.MessageSource;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Random;
/**
* Engine for controlling the aspects of the city government. This engine handles all cities.
* @author Andi Hotz, (c) Sahits GmbH, 2015
* Created on Mar 14, 2015
*/
@LazySingleton
@ClassCategory(EClassCategory.SINGLETON_BEAN)
public class CityHallEngine extends AbstractEngine {
// Taxes per 100 citizens
private double weeklyHeadTaxPoor = 0;
private double weeklyHeadTaxMiddleClass = 0.6;
private double weeklyHeadTaxRich = 2.0;
// property tax on the building
// method for tax value per citizen based on social rank
@Autowired
@Qualifier("serverClientEventBus")
private AsyncEventBus clientServerEventBus;
@Autowired
private StateFactory stateFactory;
@Autowired
private Random rnd;
@Autowired
private Date date;
@Autowired
private CityUtilities citiesInteractionService;
@Autowired
private IMap map;
@Autowired
private SeaPiratesState pirateState;
@Autowired
private LoanerEngine loanerEngine;
@Autowired
private EngineFactory engineFactory;
@Autowired
private PeopleFactory peopleFactory;
@Autowired
private TimedUpdatableTaskList timedTaskListener;
@Autowired
@Qualifier("clientEventBus")
private AsyncEventBus clientEventBus;
@Autowired
private Locale locale;
@Autowired
private MessageSource messageSource;
@Autowired
private EldermanCandidateList eldermanCandidates;
@Autowired
private CityHallList cityHalls;
private CityInitialisation initialisation = new CityInitialisation();
@Autowired
private EldermanTaskPlayerMap takenTasks;
@PostConstruct
private void init() {
// todo: andi 3/22/15: listen to outrigger changes to steer the captains skills improvement
clientServerEventBus.register(this);
}
@PreDestroy
private void unregister() {
clientServerEventBus.unregister(this);
}
@Subscribe
public void handleWeeklyUpdate(PeriodicalTimeWeekEndUpdate event) {
for (ICityHall cityHall : cityHalls) {
updateTreasuryWeekly(cityHall);
List newNotices = citiesInteractionService.createNotices(cityHall.getCity());
cityHall.getNotices().clear();
cityHall.getNotices().addAll(newNotices);
checkPetitions(cityHall);
if (cityHall.getEldermanOffice().isPresent()) {
checkTasksFinished(cityHall.getEldermanOffice().get());
checkViolations(cityHall.getEldermanOffice().get(), cityHall.getElderman(), cityHall);
}
}
}
private void checkPetitions(ICityHall cityHall) {
if (cityHall.getPetition().isPresent()) {
DateTime meeting = date.getCurrentDate().plusDays(20 + rnd.nextInt(40));
((CityHall)cityHall).setNextCouncilMeeting(Optional.of(meeting));
}
ITreasury treasury = cityHall.getTreasury();
if (treasury.getCash() < 1000) {
int random = rnd.nextInt(10);
if (random == 0) {
IEldermanOffice office = getEldermanOffice();
random = rnd.nextInt(3);
final ICity city = cityHall.getCity();
if (random == 0) {
// Special tax
ISpecialTaxViolation violation = new SpecialTaxViolation(city, date.getCurrentDate());
DateTime deadline = date.getCurrentDate().plusDays(rnd.nextInt(50));
engineFactory.getViolationTask(violation, office, deadline);
leveySpecialTax(cityHall, new SpecialTaxPetition(20000));
}
if (random == 1) {
// Custom tax
ICustomsViolation violation = new CustomsViolation(city, date.getCurrentDate());
DateTime deadline = date.getCurrentDate().plusDays(rnd.nextInt(50));
engineFactory.getViolationTask(violation, office, deadline);
}
if (random == 2) {
// plunder trading offices
IPlunderTradingOfficesViolation violation = new PlunderTradingOfficeViolation(city, date.getCurrentDate());
DateTime deadline = date.getCurrentDate().plusDays(rnd.nextInt(50));
engineFactory.getViolationTask(violation, office, deadline);
List buildings = city.findBuilding(ITradingOffice.class, Optional.empty());
for (ITradingOffice trOffice : buildings) {
for (EWare ware : EWare.values()) {
AmountablePrice amountable = trOffice.getWare(ware);
trOffice.move(ware, -amountable.getAmount());
city.move(ware,amountable.getAmount(), cityHall.getMayor());
}
}
}
}
}
}
private void checkViolations(IEldermanOffice office, ICitizen elderman, ICityHall cityHall) {
if (office.getViolation().isPresent()) {
final CityViolation violation = (CityViolation) office.getViolation().get();
if (elderman instanceof IPlayer) {
if (!(elderman instanceof IAIPlayer)) {
if (violation.getDate().plusMonths(2).isBefore(date.getCurrentDate()) && violation.getPunishment() == null) {
String s = messageSource.getMessage("ch.sahits.game.openpatrician.engine.land.city.CityHallEngine.missedViolation", new Object[]{violation.getCity().getName()}, locale.getCurrentLocal()); // todo: andi 6/7/15: the city name must be language neutral
DisplayMessage msg = new DisplayMessage(s);
clientEventBus.post(msg);
for (ICity city : map.getCities()) {
city.getReputation((IPlayer) elderman).update(-10);
}
((EldermanOffice) office).setViolation(Optional.empty());
}
return; // Nothing more to check for human elderman
}
}
if (elderman instanceof IAIPlayer) {
if (violation.getDate().plusMonths(2).isBefore(date.getCurrentDate()) && violation.getPunishment() == null) {
for (ICity city : map.getCities()) {
city.getReputation((IPlayer) elderman).update(-10);
}
}
}
if (violation.getDate().plusMonths(2).isBefore(date.getCurrentDate()) && violation.getPunishment() == null) {
((EldermanOffice) office).setViolation(Optional.empty());
}
if (!violation.getDate().plusMonths(2).isBefore(date.getCurrentDate())) {
// take measure
if (elderman.getHometown().equals(violation.getCity())) {
violation.setPunishment(ECityViolationPunishment.NONE);
} else {
int index = rnd.nextInt(ECityViolationPunishment.values().length);
violation.setPunishment(ECityViolationPunishment.values()[index]);
}
DateTime meeting = date.getCurrentDate().plusDays(20 + rnd.nextInt(40));
((CityHall)cityHall).setHanseaticMeetingDate(Optional.of(meeting));
}
}
}
private void checkTasksFinished(IEldermanOffice office) {
List tasks = office.getWorkedOnTasks();
DateTime now = date.getCurrentDate();
for (Iterator iterator = tasks.iterator(); iterator.hasNext(); ) {
IAcceptedEldermanTask task = iterator.next();
boolean updateStanding = false;
if (now.isBefore(task.getDeadline())) {
updateStanding = true;
}
if (task.getTask() instanceof IHelpCity) {
IHelpCity concreteTask = (IHelpCity) task.getTask();
final ICity city = concreteTask.getCity();
if (city.getPopulationBinding().get() >= 2000) {
if (updateStanding) {
for (ICity iCity : map.getCities()) {
if (iCity.equals(city)) {
iCity.getReputation(task.getPlayer()).update(50);
} else {
iCity.getReputation(task.getPlayer()).update(20);
}
}
}
iterator.remove();
}
}
if (task.getTask() instanceof IBuildLandPassage) {
IBuildLandPassage concreteTask = (IBuildLandPassage) task.getTask();
ICity city1 = concreteTask.getFromCity();
ICity city2 = concreteTask.getToCity();
if (!city1.findBuilding(IBarn.class, Optional.empty()).isEmpty() &&
!city2.findBuilding(IBarn.class, Optional.empty()).isEmpty()) {
if (updateStanding) {
for (ICity iCity : map.getCities()) {
if (iCity.equals(city1)) {
iCity.getReputation(task.getPlayer()).update(70);
} else if (iCity.equals(city2)) {
iCity.getReputation(task.getPlayer()).update(70);
} else {
iCity.getReputation(task.getPlayer()).update(35);
}
}
}
iterator.remove();
}
}
if (task.getTask() instanceof IFoundNewSettlement) {
IFoundNewSettlement concreteTask = (IFoundNewSettlement) task.getTask();
ICity city = map.findCity(concreteTask.getName());
Optional absent = Optional.empty();
if (city.getPopulationBinding().get() > 1000 &&
!city.findBuilding(ICityWall.class, absent).isEmpty() &&
!city.findBuilding(IMarketplace.class, absent).isEmpty() &&
!city.findBuilding(ch.sahits.game.openpatrician.model.building.ICityHall.class, absent).isEmpty() &&
!city.findBuilding(ITradingOffice.class, Optional.of(task.getPlayer())).isEmpty()) {
if (updateStanding) {
for (ICity iCity : map.getCities()) {
if (iCity.equals(city)) {
iCity.getReputation(task.getPlayer()).update(200);
} else {
iCity.getReputation(task.getPlayer()).update(100);
}
}
}
iterator.remove();
}
}
if (task.getTask() instanceof ICapturePirateNest) {
ICapturePirateNest concreteTask = (ICapturePirateNest) task.getTask();
for (PirateNest pirateNest : map.getPirateNests()) {
if (pirateNest.getLocation().equals(concreteTask.getLocation()) &&
pirateNest.getDefendingShips().isEmpty()) {
// todo: andi 6/6/15: also check the other armarments
if (updateStanding) {
for (ICity iCity : map.getCities()) {
iCity.getReputation(task.getPlayer()).update(80);
}
}
iterator.remove();
}
}
}
}
}
@Subscribe
public void handleDailyUpdate(PeriodicalTimeDayUpdate event) {
for (ICityHall cityHall : cityHalls) {
// There was a ballot the day before
if (cityHall.getBallotResult().isPresent()) {
Ballot result = (Ballot) cityHall.getBallotResult().get();
if (result.getPetition() != null) {
ICityPetition petition = result.getPetition();
if (result.getNumberNo() > result.getNumberYes()) {
handleDeniedCityPetition(cityHall, petition);
} else {
handleAcceptedCityPetition(cityHall, petition);
}
((CityHall)cityHall).setBallotResult(Optional.empty());
((CityHall)cityHall).setNextCouncilMeeting(Optional.empty());
} else {
ICityViolation violation = result.getViolation();
if (result.getNumberNo() > result.getNumberYes()) {
handleDeniedCityViolation(violation);
} else {
handleCityViolationPunishment(violation);
}
((CityHall)cityHall).setBallotResult(Optional.empty());
((CityHall)cityHall).setHanseaticMeetingDate(Optional.empty());
}
}
// There was an election the previous date
if (cityHall.getElectionResult().isPresent()) {
notificationElectionWinner(cityHall);
}
// Check if today is an election or ballot
if (date.isToday(cityHall.getElectionDate())) {
electNewMayor(cityHall);
} else if (cityHall.getEldermanOffice().isPresent() && date.isToday(cityHall.getEldermanElectionDate())) {
electNewElderman(cityHall);
} else if (cityHall.getNextCouncilMeeting().isPresent() && cityHall.getPetition().isPresent() && date.isToday(cityHall.getNextCouncilMeeting().get())) {
voteOnPetition(cityHall);
} else if (cityHall.getEldermanOffice().isPresent() && cityHall.getHanseaticMeetingDate().isPresent() && cityHall.getEldermanOffice().get().getViolation().isPresent() && date.isToday(cityHall.getHanseaticMeetingDate().get())){
voteOnViolation(cityHall);
}
}
}
private void voteOnViolation(ICityHall cityHall) {
DateTime now = date.getCurrentDate();
ICityViolation violation = cityHall.getEldermanOffice().get().getViolation().get();
Ballot result = new Ballot(violation);
((CityHall)cityHall).setBallotResult(Optional.of(result));
int votingTimeFrame = 18*60;
for (ICity city : map.getCities()) {
ICityHall ch = findCityHall(city);
ICitizen mayor = ch.getMayor();
if (mayor instanceof IPlayer) { // Human player
if (!(mayor instanceof IAIPlayer)) {
continue;
}
}
DateTime executionTime = now.plusMinutes(rnd.nextInt(votingTimeFrame));
int random = rnd.nextInt(100);
int limit = getLimit(city, violation);
boolean yes = random <= limit;
VoteTask task = engineFactory.getVoteTask(yes, executionTime, result);
timedTaskListener.add(task);
}
}
private void voteOnPetition(ICityHall cityHall) {
DateTime now = date.getCurrentDate();
ICityPetition petition = cityHall.getPetition().get();
Ballot result = new Ballot(petition);
((CityHall)cityHall).setBallotResult(Optional.of(result));
List councilmen = cityHall.getCouncilmen();
int votingTimeFrame = 18*60;
for (ICitizen citizen : councilmen) {
if (citizen instanceof IPlayer) { // Human player
if (!(citizen instanceof IAIPlayer)) {
continue;
}
}
DateTime executionTime = now.plusMinutes(rnd.nextInt(votingTimeFrame));
int random = rnd.nextInt(100);
int limit = getLimit(cityHall, petition);
boolean yes = random <= limit;
VoteTask task = engineFactory.getVoteTask(yes, executionTime, result);
timedTaskListener.add(task);
}
}
/**
* Calculate the limit for a petition.
* @param cityHall
* @param petition
* @return
*/
private int getLimit(ICityHall cityHall, ICityPetition petition) {
if (petition instanceof ICityWallPetition) {
int population = cityHall.getCity().getPopulationBinding().get();
ECityWall cityWall = cityHall.getCity().getCityState().getCityWall().getExtension();
switch (cityWall) {
case NOT_EXTENDED:
if (population < 8000) return 70;
if (population < 15000) return 95;
return 100;
case EXTENDED_ONCE:
if (population < 8000) return -1;
if (population < 15000) return 70;
return 95;
case EXTENDED_TWICE: // cannot extend more
if (population < 8000) return -1;
if (population < 15000) return -1;
return -1;
}
} else if (petition instanceof IHeadTaxPetition){
long cash = cityHall.getTreasury().getCash();
double currentHeadTax = cityHall.getTreasury().getCurrentHeadTaxValue();
if (cash < 10000) {
if (((IHeadTaxPetition)petition).getNewTaxValue() > currentHeadTax) {
return 60;
} else {
if (cash < 5000) {
return 20;
} else {
return 50;
}
}
} else {
if (((IHeadTaxPetition)petition).getNewTaxValue() > currentHeadTax) {
return 0;
} else {
return 60;
}
}
} else if (petition instanceof IMilitiaPetition) {
int population = cityHall.getCity().getPopulationBinding().get();
int nbGuards = cityHall.getMaxNumberMilita();
if (population/100 >= nbGuards+5) {
return 70;
} else if (population/100 >= nbGuards) {
return 55;
} else {
return 30;
}
} else if (petition instanceof ISpecialTaxPetition) {
long cash = cityHall.getTreasury().getCash();
if (cash < 2000) {
return 65;
} else if (cash < 5000) {
return 50;
} else {
return 20;
}
}
return -1;
}
/**
* Calculate the limit for a violation.
* @param city
* @param violation
* @return
*/
private int getLimit(ICity city, ICityViolation violation) {
if (city.equals(violation.getCity())) {
if (violation.getPunishment() == ECityViolationPunishment.NONE) {
return 100;
} else {
return -1;
}
}
if ( violation instanceof ICustomsViolation) {
switch (violation.getPunishment()) {
case NONE:
return 10;
case SMALL_FINE:
return 100;
case MEDIUM_FINE:
return 65;
case LARGE_FINE:
return 45;
case BLOCKADE:
return 15;
}
} else if(violation instanceof IPirateSupportViolation) {
switch (violation.getPunishment()) {
case NONE:
return 15;
case SMALL_FINE:
return 100;
case MEDIUM_FINE:
return 85;
case LARGE_FINE:
return 70;
case BLOCKADE:
return 40;
}
} else if (violation instanceof IPlunderTradingOfficesViolation) {
switch (violation.getPunishment()) {
case NONE:
return 10;
case SMALL_FINE:
return 100;
case MEDIUM_FINE:
return 95;
case LARGE_FINE:
return 90;
case BLOCKADE:
return 70;
}
} else if (violation instanceof SpecialTaxViolation) {
switch (violation.getPunishment()) {
case NONE:
return 8;
case SMALL_FINE:
return 100;
case MEDIUM_FINE:
return 90;
case LARGE_FINE:
return 65;
case BLOCKADE:
return 15;
}
}
return -1;
}
private void electNewElderman(ICityHall cityHall) {
// Add the election object
Election electionResult = new Election(true);
((CityHall)cityHall).setElectionResult(Optional.of(electionResult));
// Create a timer for all councilmen over the next 18h
DateTime now = date.getCurrentDate();
// each councilmen selects one of the list using OpenPatricianRandom
int votingTimeFrame = 18*60;
for (ICity city : map.getCities()) {
ICityHall ch = findCityHall(city);
ICitizen mayor = ch.getMayor();
if (mayor instanceof IPlayer) { // Human player
if (!(mayor instanceof IAIPlayer)) {
continue;
}
} // Create a SortedMapRandomizedSameElement of the candidates
final EldermanCandidateList eldermanCandidates = cityHall.getEldermanCandidates();
SortedMapRandomizedSameElements mappedCandidates = citiesInteractionService.getCandidateMap(eldermanCandidates.getAll(), city);
DateTime executionTime = now.plusMinutes(rnd.nextInt(votingTimeFrame));
ElectionTask task = engineFactory.getNewElectionTask(mappedCandidates, electionResult, executionTime);
timedTaskListener.add(task);
}
}
private void electNewMayor(ICityHall cityHall) {
// Create a SortedMapRandomizedSameElement of the candidates
SortedMapRandomizedSameElements mappedCandidates = citiesInteractionService.getCandidateMap(cityHall.getCandidates(), cityHall.getCity());
// Add the election object
Election electionResult = new Election(true);
((CityHall)cityHall).setElectionResult(Optional.of(electionResult));
// Create a timer for all councilmen over the next 18h
DateTime now = date.getCurrentDate();
// each councilmen selects one of the list using OpenPatricianRandom
List councilmen = cityHall.getCouncilmen();
int votingTimeFrame = 18*60;
for (ICitizen citizen : councilmen) {
if (citizen instanceof IPlayer) { // Human player
if (!(citizen instanceof IAIPlayer)) {
continue;
}
}
DateTime executionTime = now.plusMinutes(rnd.nextInt(votingTimeFrame));
ElectionTask task = engineFactory.getNewElectionTask(mappedCandidates, electionResult, executionTime);
timedTaskListener.add(task);
}
}
/**
* Publish a message with the winner of the election.
* @param cityHall
*/
private void notificationElectionWinner(ICityHall cityHall) {
DateTime newDate = date.getCurrentDate().plusYears(2).minusDays(1);
Election result = (Election) cityHall.getElectionResult().get();
ICitizen winner = null;
int maxVotes = 0;
for (Entry entry : result.getVotes().entrySet()) {
if (entry.getValue() > maxVotes) {
winner = entry.getKey();
maxVotes = entry.getValue();
}
}
String name = winner.getName()+" "+winner.getLastName();
if (result.isMayoral()) {
String s = messageSource.getMessage("ch.sahits.game.openpatrician.engine.land.city.CityHallEngine.mayoralElectionResult", new Object[]{cityHall.getCity().getName(), name}, locale.getCurrentLocal());
DisplayMessage msg = new DisplayMessage(s);
clientEventBus.post(msg);
((CityHall)cityHall).setMayor(winner);
((CityHall) cityHall).setElectionDate(newDate);
} else {
String s = messageSource.getMessage("ch.sahits.game.openpatrician.engine.land.city.CityHallEngine.aldermanElectionResult", new Object[]{cityHall.getCity().getName(), name}, locale.getCurrentLocal());
DisplayMessage msg = new DisplayMessage(s);
clientEventBus.post(msg);
((CityHall)cityHall).setElderman(winner);
((CityHall) cityHall).setEldermanElectionDate(newDate);
}
((CityHall) cityHall).setElectionResult(Optional.empty());
}
/**
* The Hanseatic council agreed on a punishment for a city violation, execute it.
* @param violation
*/
private void handleCityViolationPunishment(ICityViolation violation) {
ECityViolationPunishment punishment = violation.getPunishment();
String message = null;
if (punishment == ECityViolationPunishment.NONE) {
message = messageSource.getMessage("ch.sahits.game.openpatrician.engine.land.city.CityHallEngine.yesViolationNoAction", new Object[]{violation.getCity().getName()}, locale.getCurrentLocal()); // todo: andi 6/7/15: the city name must be language neutral
// Nothing to be done
} else if (punishment == ECityViolationPunishment.BLOCKADE) {
message = messageSource.getMessage("ch.sahits.game.openpatrician.engine.land.city.CityHallEngine.yesViolationBlockade", new Object[]{violation.getCity().getName()}, locale.getCurrentLocal()); // todo: andi 6/7/15: the city name must be language neutral
// todo: andi 4/24/15: call for blockade ships. See ticket #202
initializeBlockadeAction(violation.getCity());
} else {
ICity otherCity = violation.getCity();
ICityHall otherCityHall = findCityHall(otherCity);
Treasury otherTreasury = (Treasury) otherCityHall.getTreasury();
int fine = citiesInteractionService.getFine(punishment, otherTreasury);
message = messageSource.getMessage("ch.sahits.game.openpatrician.engine.land.city.CityHallEngine.yesViolationFine", new Object[]{violation.getCity().getName(), fine}, locale.getCurrentLocal()); // todo: andi 6/7/15: the city name must be language neutral
otherTreasury.subtractOtherCosts(fine);
int cashPerCity = fine/(map.getNumberCities() - 1);
for (ICity city : map.getCities()) {
if (city.equals(otherCity)) {
continue;
}
Treasury treasury = (Treasury) findCityHall(city).getTreasury();
treasury.addOtherIncome(cashPerCity);
}
}
DisplayMessage msg = new DisplayMessage(message);
clientEventBus.post(msg);
}
/**
* The Hanseatic council decided not to punish a city for its violation, notify.
* @param violation
*/
private void handleDeniedCityViolation(ICityViolation violation) {
String message = null;
if (violation instanceof ICustomsViolation) {
message = messageSource.getMessage("ch.sahits.game.openpatrician.engine.land.city.CityHallEngine.noSanctionsCustomViolation", new Object[]{violation.getCity().getName()}, locale.getCurrentLocal());
}
if (violation instanceof IPirateSupportViolation) {
message = messageSource.getMessage("ch.sahits.game.openpatrician.engine.land.city.CityHallEngine.noSanctionsPirateSupport", new Object[]{violation.getCity().getName()}, locale.getCurrentLocal());
}
if (violation instanceof IPlunderTradingOfficesViolation) {
message = messageSource.getMessage("ch.sahits.game.openpatrician.engine.land.city.CityHallEngine.noSanctionsPlunder", new Object[]{violation.getCity().getName()}, locale.getCurrentLocal());
}
if (violation instanceof ISpecialTaxViolation) {
message = messageSource.getMessage("ch.sahits.game.openpatrician.engine.land.city.CityHallEngine.noSanctionsSpecialTaxViolation", new Object[]{violation.getCity().getName()}, locale.getCurrentLocal());
}
DisplayMessage msg = new DisplayMessage(message);
clientEventBus.post(msg);
}
/**
* The city council accepted a petition, execute it.
* @param cityHall
* @param petition
*/
private void handleAcceptedCityPetition(final ICityHall cityHall, ICityPetition petition) {
String message = null;
if (petition instanceof ICityWallPetition) {
message = messageSource.getMessage("ch.sahits.game.openpatrician.engine.land.city.CityHallEngine.yesCityWall", new Object[]{cityHall.getCity().getName()}, locale.getCurrentLocal());
CityWall cityWall = cityHall.getCity().getCityState().getCityWall();
if (cityWall.getExtension() == ECityWall.NOT_EXTENDED) {
ECityWall next = ECityWall.EXTENDED_ONCE;
cityWall.setRequiredBricks(next.getRequiredBricks());
cityWall.setBoughtBricks(0);
cityWall.setUsedBricks(0);
cityWall.setExtension(next);
} else if (cityWall.getExtension() == ECityWall.EXTENDED_ONCE) {
ECityWall next = ECityWall.EXTENDED_TWICE;
cityWall.setRequiredBricks(next.getRequiredBricks());
cityWall.setBoughtBricks(0);
cityWall.setUsedBricks(0);
cityWall.setExtension(next);
}
}
if (petition instanceof IHeadTaxPetition) {
double newTax = ((IHeadTaxPetition)petition).getNewTaxValue();
message = messageSource.getMessage("ch.sahits.game.openpatrician.engine.land.city.CityHallEngine.yesHeadTax", new Object[]{cityHall.getCity().getName(), newTax}, locale.getCurrentLocal());
((Treasury)cityHall.getTreasury()).setCurrentHeadTaxValue(newTax);
}
if (petition instanceof IMilitiaPetition) {
message = messageSource.getMessage("ch.sahits.game.openpatrician.engine.land.city.CityHallEngine.yesMilitia", new Object[]{cityHall.getCity().getName()}, locale.getCurrentLocal());
((CityHall)cityHall).setMaxNumberMilita(cityHall.getMaxNumberMilita()+5);
}
if (petition instanceof ISpecialTaxPetition) {
message = leveySpecialTax(cityHall, (ISpecialTaxPetition) petition);
}
DisplayMessage msg = new DisplayMessage(message);
clientEventBus.post(msg);
}
private String leveySpecialTax(final ICityHall cityHall, ISpecialTaxPetition petition) {
String message;
int taxHeigth = petition.getTaxValue();
message = messageSource.getMessage("ch.sahits.game.openpatrician.engine.land.city.CityHallEngine.yesSpecialTax", new Object[]{cityHall.getCity().getName(), taxHeigth}, locale.getCurrentLocal());
// 50% of the value from all players based on their cash volume.
List players = cityHall.getCity().getResidentPlayers();
long totalCashVolume = 0;
for (IPlayer player : players) {
totalCashVolume += player.getCompany().getCash();
}
long collected = taxHeigth/2;
for (IPlayer player : players) {
double percentage = player.getCompany().getCash()*1.0/totalCashVolume * 0.5;
int amount = (int) (taxHeigth * percentage);
if (player instanceof IAIPlayer) {
player.getCompany().updateCash(-amount);
collected += amount;
} else {
// Add debt to the loaner
ILoaner loaner = loanerEngine.getLoaner(cityHall.getCity());
ICreditor creditor = new ICreditor() {
@Override
public void receiveSum(long amount) {
((Treasury)cityHall.getTreasury()).addPaidSpecialTaxes(amount);
}
};
Debt debt = Debt.builder()
.loanTakeOut(date.getCurrentDate())
.debitor(player)
.creditor(creditor)
.interest(0)
.amount(amount)
.dueDate(date.getCurrentDate().plusMonths(1))
.build();
loaner.addDebt(debt);
// todo: andi 4/24/15: send out notification once #203 is implemented
}
}
((Treasury)cityHall.getTreasury()).addPaidSpecialTaxes(collected);
return message;
}
/**
* The city council decided against a petition, inform.
* @param cityHall
* @param petition
*/
private void handleDeniedCityPetition(ICityHall cityHall, ICityPetition petition) {
String message = null;
if (petition instanceof ICityWallPetition) {
message = messageSource.getMessage("ch.sahits.game.openpatrician.engine.land.city.CityHallEngine.noPetitionCityWall", new Object[]{cityHall.getCity().getName()}, locale.getCurrentLocal());
}
if (petition instanceof IHeadTaxPetition) {
message = messageSource.getMessage("ch.sahits.game.openpatrician.engine.land.city.CityHallEngine.noPetitionHeadTax", new Object[]{cityHall.getCity().getName()}, locale.getCurrentLocal());
}
if (petition instanceof IMilitiaPetition) {
message = messageSource.getMessage("ch.sahits.game.openpatrician.engine.land.city.CityHallEngine.noPetitionMilitia", new Object[]{cityHall.getCity().getName()}, locale.getCurrentLocal());
}
if (petition instanceof ISpecialTaxPetition) {
message = messageSource.getMessage("ch.sahits.game.openpatrician.engine.land.city.CityHallEngine.noPetitionSpecialTax", new Object[]{cityHall.getCity().getName()}, locale.getCurrentLocal());
}
DisplayMessage msg = new DisplayMessage(message);
clientEventBus.post(msg);
}
private void initializeBlockadeAction(ICity city) {
// Issue call for ships (fleet of 10-20 => 1 ship, 20-30 => 2 ships, more 3 ships)
// At the date of assembly build convoy to ship to the blocked city
// For any ship not present fine the owner
// When at the city start a blockade for (60 - 180 days)
// When event ShipNearingPortEvent engage in battle
}
@Subscribe
public void handleMonthlyUpdate(PeriodicalTimeMonthEndUpdate event) {
for (ICityHall cityHall : cityHalls) {
handleMayoralTasks(cityHall);
if (cityHall.getEldermanOffice().isPresent()) {
handleEldermansTask(cityHall.getEldermanOffice().get());
}
}
}
private void handleEldermansTask(IEldermanOffice office) {
// update tasks
updateEldermanTasks(office);
}
private void updateEldermanTasks(IEldermanOffice office) {
List tasks = office.getTasks();
if (tasks.size() < 3) {
boolean helpTask = false;
boolean newTown = false;
boolean landPassage = false;
boolean huntPirate = false;
boolean capturePirateNest = false;
for (IEldermanTask task : tasks) {
if (task instanceof IHelpCity) {
helpTask = true;
continue;
}
if (task instanceof IHuntPirate) {
huntPirate = true;
continue;
}
if (task instanceof IBuildLandPassage) {
landPassage = true;
continue;
}
if (task instanceof IFoundNewSettlement) {
newTown = true;
continue;
}
if (task instanceof ICapturePirateNest) {
capturePirateNest = true;
continue;
}
} // end for
if (!helpTask) {
Optional task = stateFactory.createHelpCityEldermanTask(office);
if (task.isPresent()) {
tasks.add(task.get());
}
}
if (tasks.size() < 3 && !huntPirate) {
Optional task = stateFactory.createPirateHuntEledermanTask(office);
if (task.isPresent()) {
tasks.add(task.get());
}
}
if (tasks.size() < 3 && !landPassage) {
Optional task = stateFactory.createNewLandBridgeEldermanTask(office);
if (task.isPresent()) {
tasks.add(task.get());
}
}
if (tasks.size() < 3 && !newTown) {
Optional task = stateFactory.createNewSettlementEledermanTask(office);
if (task.isPresent()) {
tasks.add(task.get());
}
}
if (tasks.size() < 3 && !capturePirateNest) {
Optional task = stateFactory.createCapturePirateNestEldermanTask(office);
if (task.isPresent()) {
tasks.add(task.get());
}
}
}
}
private void handleMayoralTasks(ICityHall cityHall) {
// Check city wall
CityWall cityWall = cityHall.getCity().getCityState().getCityWall();
boolean isPlayer = isPlayer(cityHall.getMayor());
if (cityWall.getBoughtBricks() < cityWall.getRequiredBricks() && !isPlayer) {
int random = rnd.nextInt(28);
DateTime exection = date.getCurrentDate().plusDays(random);
CityWallMaterialBuyingTask task = engineFactory.getCityWallBuyMaterialTask(cityHall, exection);
timedTaskListener.add(task);
}
// Check militia
if (cityHall.getMaxNumberMilita() > cityHall.getCityGuard().size() && !isPlayer) {
int random = rnd.nextInt(4);
if (random == 0) {
int[] guardNumbers = new int[4];
guardNumbers[0] = countMilitia(cityHall.getCityGuard(), IBowmen.class);
guardNumbers[1] = countMilitia(cityHall.getCityGuard(), IPikemen.class);
guardNumbers[2] = countMilitia(cityHall.getCityGuard(), ICrossbowmen.class);
guardNumbers[3] = countMilitia(cityHall.getCityGuard(), IMusketeer.class);
int whatIndex = 0;
if (guardNumbers[0] > guardNumbers[1]) {
whatIndex = 1;
}
if (guardNumbers[whatIndex] > guardNumbers[2]) {
whatIndex = 2;
}
if (guardNumbers[whatIndex] > guardNumbers[3]) {
whatIndex = 3;
}
Treasury treasury = ((Treasury)cityHall.getTreasury());
switch (whatIndex) {
case 0:
treasury.subtractCityGuardCosts(5); // todo: andi 6/5/15: do the transaction with the blacksmith
IBowmen bowman = peopleFactory.createBowman();
cityHall.getCityGuard().add(bowman);
break;
case 1:
treasury.subtractCityGuardCosts(5); // todo: andi 6/5/15: do the transaction with the blacksmith
IPikemen pikeman = peopleFactory.createPikeman();
cityHall.getCityGuard().add(pikeman);
break;
case 2:
treasury.subtractCityGuardCosts(10); // todo: andi 6/5/15: do the transaction with the blacksmith
ICrossbowmen crossbowman = peopleFactory.createCrossbowman();
cityHall.getCityGuard().add(crossbowman);
break;
case 3:
treasury.subtractCityGuardCosts(20); // todo: andi 6/5/15: do the transaction with the blacksmith
IMusketeer musketeer = peopleFactory.createMusketeer();
cityHall.getCityGuard().add(musketeer);
break;
default:
throw new IllegalStateException("Case "+whatIndex+" is not handled");
}
}
}
}
private int countMilitia(List cityGuard, Class extends ICityGuard> clazz) {
int count = 0;
for (ICityGuard guard : cityGuard) {
if (clazz == IBowmen.class) {
if (guard instanceof IBowmen) {
count++;
}
}
if (clazz == ICrossbowmen.class) {
if (guard instanceof ICrossbowmen) {
count++;
}
}
if (clazz == IPikemen.class) {
if (guard instanceof IPikemen) {
count++;
}
}
if (clazz == IMusketeer.class) {
if (guard instanceof IMusketeer) {
count++;
}
}
}
return count;
}
private boolean isPlayer(ICitizen mayor) {
if (mayor instanceof IPlayer) {
if (mayor instanceof IAIPlayer) {
return false;
}
return true;
}
return false;
}
@Subscribe
public void handleEndOfYearUpdate(PeriodicalTimeYearEndUpdate event) {
for (ICityHall cityHall : cityHalls) {
}
}
@Override
public List getChildren() {
return new ArrayList<>();
}
public void establishCityHall(ICity city) {
if (initialisation.initialisationHappened) {
initializeCity(city);
} else {
initialisation.cities.add(city);
}
}
private ICityHall initializeCity(ICity city) {
ICityHall cityHall = stateFactory.createCityHall(city);
return cityHall;
}
@Subscribe
public void handleGameStartEvent(NewGameClient newGameClient) {
initialisation.initialisationHappened = true;
for (int i = 0; i < initialisation.cities.size(); i++) {
ICity city = initialisation.cities.get(i);
initializeCity(city);
}
for (ICityHall cityHall : cityHalls) {
if (eldermanCandidates.size() < 4) {
eldermanCandidates.add(cityHall.getMayor());
}
}
ICitizen elderman = eldermanCandidates.get(rnd.nextInt(eldermanCandidates.size()));
DateTime election = date.getCurrentDate().plusDays(rnd.nextInt(600));
for (ICityHall cityHall : cityHalls) {
((CityHall)cityHall).setElderman(elderman);
((CityHall)cityHall).setEldermanElectionDate(election);
if (elderman.getHometown().equals(cityHall.getCity())) {
Optional office = Optional.of(stateFactory.createEldermanOffice());
((CityHall) cityHall).setEldermanOffice(office);
}
}
}
private static class CityInitialisation {
private List cities = new ArrayList<>();
private boolean initialisationHappened = false;
}
public ICityHall findCityHall(ICity city) {
for (ICityHall cityHall : cityHalls) {
final ICity cityHallCity = cityHall.getCity();
if (cityHallCity.equals(city)) {
return cityHall;
}
}
return null;
}
public void assignTask(IPlayer player, IEldermanTask task) {
takenTasks.assignTask(player, task);
}
/**
* Update the treasury. Payed taxes and recurring costs (militia, outrigger).
* This update is done on a weekly basis.
*/
@VisibleForTesting
void updateTreasuryWeekly(ICityHall cityHall) {
Treasury treasury = (Treasury) cityHall.getTreasury();
treasury.reset();
ICity city = cityHall.getCity();
if (cityHall.getOutriggerContract().isPresent()) {
final IOutriggerContract outriggerContract = cityHall.getOutriggerContract().get();
final int weeklyRefund = outriggerContract.getWeeklyRefund();
treasury.subtractOutriggerCosts(weeklyRefund);
IPlayer owner = (IPlayer) outriggerContract.getOutrigger().getOwner();
owner.getCompany().updateCash(weeklyRefund);
}
int population = city.getPopulation(EPopulationClass.POOR);
double taxes = population/100*weeklyHeadTaxPoor*treasury.getCurrentHeadTaxValue();
population = city.getPopulation(EPopulationClass.MEDIUM);
taxes += population/100*weeklyHeadTaxMiddleClass*treasury.getCurrentHeadTaxValue();
population = city.getPopulation(EPopulationClass.RICH);
taxes += population/100*weeklyHeadTaxRich*treasury.getCurrentHeadTaxValue();
List residents = city.getResidentPlayers();
long propertyTax = 0;
for (IPlayer resident : residents) {
final ICompany company = resident.getCompany();
long cash = company.getCash();
double weeklyPercentage = treasury.getCurrentHeadTaxValue()/(100.0*52);
long taxAmount = (long)(cash*weeklyPercentage);
taxes +=taxAmount;
List buildings = resident.findBuildings(city);
long propertyTaxPerResedident = 0;
for (IBuilding building : buildings) {
propertyTaxPerResedident += building.getPropertyTax() * treasury.getCurrentPropertyTax();
}
propertyTax += propertyTaxPerResedident;
company.updateCash(-(taxAmount+propertyTaxPerResedident));
ICity hometown = resident.getHometown();
BalanceSheet sheet = (BalanceSheet) resident.findTradingOffice(hometown).getCurrentWeek();
sheet.deductPropertyTaxes((int)(taxAmount+propertyTaxPerResedident));
}
treasury.addPaidTaxes((long)taxes+propertyTax);
List guards = cityHall.getCityGuard();
int guardCosts = 0;
for (ICityGuard guard : guards) {
guardCosts += guard.getAmount() * guard.getWeeklySalary();
}
treasury.subtractCityGuardCosts(guardCosts);
}
/**
* Check if the destroyed ship was a pirate ship that belongs to an elderman task.
* @param event
*/
public void checkPirateKilledEldermanTask(ShipAttackEvent event) {
if (event.getAttackedShip().getOwner() instanceof ISeaPirate) {
IEldermanOffice office = getEldermanOffice();
for (Iterator iterator = office.getWorkedOnTasks().iterator(); iterator.hasNext(); ) {
IAcceptedEldermanTask task = iterator.next();
if (task.getTask() instanceof IHuntPirate) {
IHuntPirate concreteTask = (IHuntPirate) task.getTask();
if (concreteTask.getPirate().equals(event.getAttackedShip().getOwner())) {
if (date.getCurrentDate().isBefore(task.getDeadline())) {
for (ICity iCity : map.getCities()) {
iCity.getReputation(task.getPlayer()).update(15);
}
}
iterator.remove();
}
}
}
}
}
private IEldermanOffice getEldermanOffice() {
IEldermanOffice office = null;
for (ICityHall cityHall : cityHalls) {
if (cityHall.getEldermanOffice().isPresent()) {
office = cityHall.getEldermanOffice().get();
break;
}
}
return office;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy