/// <summary> /// Opens an outlet at a given position on a settlement (or a random one). /// </summary> public void OpenOutlet(Vector2 position = null) { //Need to generate a random position? if (position == null) { position = settlement.GetFreeRandomPosition(); } else { //Check if the position is occupied. if (settlement.IsOccupied(position)) { throw new Exception("Cannot open an outlet there, the space is occupied."); } } //Sufficient balance? if (Balance < OutletCost) { throw new Exception("Cannot open an outlet, insufficient company balance."); } //Open the outlet. Balance -= Math.Round(OutletCost, 2); outlets.Add(new Outlet(settlement, this, position, OutletCapacity, DailyCost)); //Log in the event chain. EventChain.AddEvent(new OutletCreateEvent() { Position = position, Capacity = OutletCapacity }); }
/// <summary> /// Visit the outlet (increments visits for today). /// </summary> public void Visit(Vector2 origin) { visitsToday++; //Log event. EventChain.AddEvent(new VisitOutletEvent() { Company = ParentCompany.Name, Household = origin, OutletPos = Position }); }
/// <summary> /// Processes the end of the day for this company. /// </summary> public void ProcessDayEnd() { //Set up locals. double deliveryCosts = 0, profitLossFromOutlets = 0; //Move all the food trucks first. foreach (var outlet in outlets) { if (outlet is FoodTruck) { //Move it. ((FoodTruck)outlet).Move(); } } //Calculate delivery costs. deliveryCosts += BaseDeliveryCost + CalculateDeliveryCost(); Balance -= Math.Round(deliveryCosts, 2); EventChain.AddEvent(new DeliveryEvent() { CompanyName = Name, Cost = Math.Round(deliveryCosts, 2), Type = EventType.Delivery }); //Calculate outlet profit and loss. foreach (var outlet in outlets) { double thisOutletProfit = outlet.GetDailyProfit(avgCostPerMeal, avgPricePerMeal); profitLossFromOutlets += thisOutletProfit; //Log an outlet's profits/losses. EventChain.AddEvent(new OutletProfitEvent() { ProfitLoss = Math.Round(thisOutletProfit, 2), Company = Name }); outlet.ProcessDayEnd(); } //Update balance. Balance += Math.Round(profitLossFromOutlets, 2); //Close the company? if (Balance <= 0) { CloseAllOutlets(); } }
/// <summary> /// Adds a random number of households (default 1-5) to the settlement. /// </summary> private void ProcessAddHouseholdsEvent() { int newHouseholds = Random.Next(Settings.Get.MinNewHouseholds, Settings.Get.MaxNewHouseholds); int stored = newHouseholds; while (newHouseholds > 0) { Settlement.AddHousehold(); newHouseholds--; } //Log to the event chain. EventChain.AddEvent(new AddHouseholdsEvent() { NumHouses = stored }); }
/// <summary> /// Moves one square in a random direction. /// </summary> public void Move() { //Calculate a random direction. int x = base.Position.x; int y = base.Position.y; int rand = Simulation.Random.Next(0, 3); switch (rand) { case 0: x--; break; case 1: x++; break; case 2: y--; break; case 3: y++; break; } //Clamp inside the settlement. x.Clamp(0, base.settlement.Width - 1); y.Clamp(0, base.settlement.Height - 1); //Is the position occupied? Don't move. if (settlement.IsOccupied(new Vector2(x, y))) { return; } //Set position, update on settlement too. Vector2 old = Position; Position = new Vector2(x, y); settlement.Unoccupy(old); settlement.Occupy(Position); //Log the event. EventChain.AddEvent(new FoodTruckMoveEvent() { From = old, To = Position, Company = base.ParentCompany.Name }); }
/// <summary> /// Process all of the leavers from the settlement. /// </summary> public void ProcessLeavers() { int removed = 0; for (int i = 0; i < Households.Count; i++) { //2% chance for a household to leave by default. if (Simulation.Random.NextDouble() < Settings.Get.ChanceOfHouseholdLeaving) { Households.RemoveAt(i); i--; removed++; } } //Log to event chain. EventChain.AddEvent(new HouseholdLeavingEvent() { NumHouseholds = removed }); }
/// <summary> /// Changes the price of fuel randomly. /// </summary> protected virtual void ProcessChangeFuelCostEvent() { //Check if fuel price increases or decreases, get by how much. bool increases = (Random.NextDouble() < Settings.Get.ChanceOfFuelPriceIncrease); double amt = Random.Next(1, 10) / 10.0; //Which company is it for? int companyIndex = Random.Next(0, Companies.Count - 1); //Complete the change, log. if (!increases) { amt = -amt; } Companies[companyIndex].ChangeFuelCostBy(amt); EventChain.AddEvent(new FuelCostChangeEvent() { AmountBy = amt, Company = Companies[companyIndex].Name, }); }
/// <summary> /// Changes a random cost for a single company by a random amount. /// </summary> private void ProcessCostChangeEvent() { //How much to change by, should it go up or down, and which company should it be applied to? bool increases = (Random.NextDouble() < Settings.Get.ChanceOfCostIncrease); double amt = Random.NextDouble() * 10.0; if (!increases) { amt = -amt; } int companyIndex = Random.Next(0, Companies.Count - 1); //Figured it out, determine which cost to increase/decrease. int costType = Random.Next(0, 1); string costTypeStr; if (costType == 0) { //Change the daily cost for the company. costTypeStr = "daily cost"; amt *= 2; Companies[companyIndex].ChangeDailyCostBy(amt); } else { //Change the average meal costs for a company. costTypeStr = "average meal cost"; Companies[companyIndex].ChangeAvgMealCostBy(amt); } //Log as an event. EventChain.AddEvent(new CostChangeEvent() { AmountBy = amt, Company = Companies[companyIndex].Name, CostType = costTypeStr }); }
/// <summary> /// Changes a single company's reputation randomly. /// </summary> private void ProcessChangeReputationEvent() { //Calculate the amount, and whether it's increasing or decreasing. bool increases = (Random.NextDouble() < Settings.Get.ChanceOfReputationIncrease); double amt = Random.Next(1, 10) / 10.0; if (!increases) { amt = -amt; } //Which company is this for? int companyIndex = Random.Next(0, Companies.Count - 1); //Apply and log. Companies[companyIndex].ChangeReputationBy(amt); EventChain.AddEvent(new ReputationChangeEvent() { Company = Companies[companyIndex].Name, AmountBy = amt }); }
/// <summary> /// Ends the current day, and moves onto the next one. /// </summary> public void ProcessDayEnd() { //Gather the cumulative reputation for all companies. double totalReputation = 0; List <double> cumulativeReputation = new List <double>(); CompaniesClosedYesterday = new List <Company>(); foreach (var c in Companies) { totalReputation += c.Reputation; cumulativeReputation.Add(totalReputation); } //Loop over each house and see if they eat out, and if so, where. visits = new List <Tuple <Vector2, Vector2> >(); CalculateVisitsFromHouseholds(cumulativeReputation, totalReputation); //Process the end of the day for each company. for (int i = 0; i < Companies.Count; i++) { Companies[i].ProcessDayEnd(); //Is the company bankrupt? if (Companies[i].Balance < 0) { //Bankrupt the company, remove from list (log event). EventChain.AddEvent(new BankruptcyEvent() { CompanyName = Companies[i].Name, DaysLasted = DaysElapsed }); CompaniesClosedYesterday.Add(Companies[i]); Companies.RemoveAt(i); i--; } } //Only do random events if companies still exist. if (Companies.Count > 0) { //Choose the random events that will occur at this day end. double eventRand = Random.NextDouble(); if (eventRand < Settings.Get.BaseChanceOfRandomEvents) { //Random household addition event. eventRand = Random.NextDouble(); if (eventRand < Settings.Get.ChanceOfAddHouseholdsEvent) { ProcessAddHouseholdsEvent(); } //Random fuel cost change event. eventRand = Random.NextDouble(); if (eventRand < Settings.Get.ChanceOfChangeFuelCostEvent) { ProcessChangeFuelCostEvent(); } //Random reputation change event. eventRand = Random.NextDouble(); if (eventRand < Settings.Get.ChanceOfReputationChangeEvent) { ProcessChangeReputationEvent(); } //Random daily cost change event. eventRand = Random.NextDouble(); if (eventRand < Settings.Get.ChanceOfCostChangeEvent) { ProcessCostChangeEvent(); } } } //Process households leaving for the day. Settlement.ProcessLeavers(); //End this day's event chain, and start a new one. EventChain.Refresh(); //Draw the new map with updated positions and tracers, if the map is enabled. UpdateMap(); DaysElapsed++; }
/// <summary> /// Calculates visits from households based on both reputation AND distance AND type of company. /// </summary> protected override void CalculateVisitsFromHouseholds(List <double> cumulativeReputation, double totalReputation) { for (int i = 0; i < Settlement.NumHouseholds; i++) { Household thisHouse = Settlement.GetHouseholdAtIndex(i); //Does this household eat out? if (!thisHouse.EatsOut()) { continue; } //Pick a type of restaurant to eat at. CompanyType picked = PickRestaurantType(); //Get outlets within a set boundary, of the selected type. double radius; switch (picked) { case CompanyType.Family: radius = Settings.Get.BalancedSim_TravelRadiusFamily; break; case CompanyType.FastFood: radius = Settings.Get.BalancedSim_TravelRadiusFastFood; break; case CompanyType.NamedChef: radius = Settings.Get.BalancedSim_TravelRadiusNamedChef; break; default: //Unknown company type. throw new Exception("Unknown company type to set travel radius for. Please update method."); } List <Outlet> outletsToPick = GetOutletsAround(thisHouse.Position, radius).Where(x => x.ParentCompany.Type == picked).ToList(); //Are there any outlets to visit? if (outletsToPick.Count == 0) { //Register a failed event. EventChain.AddEvent(new FailedToEatOutEvent() { Origin = thisHouse.Position, Radius = radius, OutletType = picked }); //This house is now done. continue; } //Get the companies that these outlets have. List <Company> companiesNearMe = new List <Company>(); foreach (var outlet in outletsToPick) { if (companiesNearMe.FindIndex(x => x.Name == outlet.ParentCompany.Name) == -1) { companiesNearMe.Add(outlet.ParentCompany); } } //Randomly roll on their reputation. double totalRep = companiesNearMe.Sum(x => x.Reputation); List <double> cumulativeRep = companiesNearMe.Select(x => x.Reputation).CumulativeSum().ToList(); double randomSelection = Random.NextDouble() * totalRep; Company selected = null; for (int k = 0; k < cumulativeRep.Count; k++) { if (randomSelection < cumulativeRep[k]) { selected = companiesNearMe[k]; break; } } //Successfully selected a company, visit! selected.AddVisitToNearestOutlet(thisHouse.Position, ref base.visits); } }
public override void OnInspectorGUI() { // Setting the GUI Styles guiStyleBox = new GUIStyle(GUI.skin.box); guiStyleBox.margin = new RectOffset(0, 20, 10, 0); guiStyleBox.padding = new RectOffset(20, 20, 10, 10); guiStyleLabel = new GUIStyle(GUI.skin.label); guiStyleLabel.richText = true; guiStyleButton = new GUIStyle(GUI.skin.button); guiStyleButton.margin = new RectOffset(0, 20, 10, 0); guiStyleButton.padding = new RectOffset(10, 10, 5, 5); // Draw chain's properties EditorGUILayout.Space(10); chain.trigger = (EventChain.EventTrigger)EditorGUILayout.EnumPopup("Trigger", chain.trigger); chain.radius = EditorGUILayout.FloatField("Radius", chain.radius); if (chain.trigger != EventChain.EventTrigger.Interact) { chain.groundedOnly = EditorGUILayout.Toggle("Grounded Only", chain.groundedOnly); } EditorGUILayout.Space(10); // For every event in the chain for (int i = 0; i < chain.events.Count; i++) { EditorGUILayout.BeginVertical(guiStyleBox); // Draw Event header EditorGUILayout.BeginHorizontal(guiStyleLabel); string label = (i + 1) + " - " + chain.events[i].GetEventName(); //if (GUILayout.Button(chain.events[i].visible? "<b>" + label + "</b>" : label, guiStyleLabel)) // chain.events[i].visible = !chain.events[i].visible; chain.events[i].visible = EditorGUILayout.Foldout(chain.events[i].visible, label); EditorGUILayout.EndHorizontal(); // Draw Event content if (chain.events[i].visible) { // Call the expecific event's editor EditorGUILayout.BeginVertical(); CreateEditor(chain.events[i]).OnInspectorGUI(); EditorGUILayout.EndVertical(); EditorGUILayout.Space(10); // Event actions EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("Move Up")) { chain.MoveEventUp(i); } if (GUILayout.Button("Move Down")) { chain.MoveEventDown(i); } if (GUILayout.Button("Duplicate")) { chain.DuplicateEvent(i); } if (GUILayout.Button("Delete")) { chain.DeleteEvent(i); } EditorGUILayout.EndHorizontal(); } EditorGUILayout.EndVertical(); } // Draw the event type dropdown EditorGUILayout.Separator(); selectedEvent = (Events)EditorGUILayout.EnumPopup("Event Type", selectedEvent); // Draw Add event button if (GUILayout.Button("Add Event", guiStyleButton)) { chain.AddEvent(GetEventType()); } EditorGUILayout.Space(10); }