-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathpassengers.lua
225 lines (192 loc) · 9.09 KB
/
passengers.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
local Constants = require("constants")
local Consumption = require("consumption")
local Util = require("util")
--- @param city City
--- @param filter string | nil
local function countCitizens(city, filter)
local total = 0
for tier, count in pairs(city.citizens) do
if filter == nil then
total = total + count
elseif filter == tier then
total = total + count
end
end
return total
end
--- @param city City
--- @param excludedNames string[] | nil
--- @return string | nil name
local function getRandomCityName(city, excludedNames)
if #(storage.tycoon_cities or {}) == 0 then
return nil
end
-- up to 10 attempts at getting a random entry that's different to the current name
for i = 1, 10, 1 do
local r = storage.tycoon_cities[city.generator(#storage.tycoon_cities)].name
if r ~= city.name then
if excludedNames ~= nil and #excludedNames > 0 then
if not Util.indexOf(excludedNames, r) then
return r
end
else
return r
end
end
end
return nil
end
--- @param cityName string
--- @return number | nil
local function get_max_departing_passengers_for_destination(cityName)
local destinationCity = nil
for id, city in ipairs(storage.tycoon_cities or {}) do
if city.name == cityName then
destinationCity = city
break
end
end
if destinationCity == nil then
return nil
end
local citizenCount = countCitizens(destinationCity)
-- This function gives us a value that scales to 2/3 at x=1000 and to 0.9 at 5000
local citizenFactor = citizenCount / (citizenCount + 500)
return math.floor(citizenCount / 10 * citizenFactor)
end
--- @param city City
local function spawnPassengers(city)
if not (game.forces.player.technologies["tycoon-public-transportation"] or {}).researched then
return
end
local residentialCount = countCitizens(city, "residential")
local highriseCount = countCitizens(city, "highrise")
local citizenCount = countCitizens(city)
-- This function gives us a value that scales to 2/3 at x=1000 and to 0.9 at 5000
local citizenFactor = citizenCount / (citizenCount + 500)
-- Residential housing have 20 citizens and highrise have 100. That means we generate up to 1 per residential and up to 5 per highrise house.
local newPassengerCount = math.floor((residentialCount * 0.05 + highriseCount * 0.05) * citizenFactor * city.generator())
if newPassengerCount > 0 then
local trainStations = Util.list_special_city_buildings(city, "tycoon-passenger-train-station")
if #trainStations > 0 then
local selectedTrainStation = trainStations[city.generator(#trainStations)]
if selectedTrainStation ~= nil and selectedTrainStation.valid then
local passengerLimit = (storage.tycoon_train_station_limits or {})[selectedTrainStation.unit_number] or 80
local departingPassengers = 0
for name, item in pairs(selectedTrainStation.get_inventory(1).get_contents()) do
local name = item.name
local count = item.count
if name ~= "tycoon-passenger-" .. string.lower(city.name) and string.find(name, "tycoon-passenger-", 1, true) then
departingPassengers = departingPassengers + count
end
end
if (departingPassengers + newPassengerCount) >= passengerLimit then
return
end
-- todo: check if train station has enough space, otherwise distribute passengers
local excludedCityNames = {}
if storage.tycoon_train_station_passenger_filters ~= nil and storage.tycoon_train_station_passenger_filters[selectedTrainStation.unit_number] ~= nil then
for cityId, state in pairs(storage.tycoon_train_station_passenger_filters[selectedTrainStation.unit_number]) do
if state == false then
table.insert(excludedCityNames, storage.tycoon_cities[cityId].name)
end
end
end
local destination = getRandomCityName(city, excludedCityNames)
if destination == nil then
return
end
local currentPassengersForDestination = 0
for name, item in pairs(selectedTrainStation.get_inventory(1).get_contents()) do
local name = item.name
local count = item.count
if name == "tycoon-passenger-" .. string.lower(destination) then
currentPassengersForDestination = currentPassengersForDestination + count
end
end
local maxForDestination = get_max_departing_passengers_for_destination(destination)
if maxForDestination == nil or maxForDestination == 0 then
return
end
local passenger = "tycoon-passenger-" .. string.lower(destination)
local insertedPassengerCount = selectedTrainStation.insert{name = passenger, count = math.min(newPassengerCount, maxForDestination)}
for player_index, _ in pairs(game.players) do
game.players[player_index].create_local_flying_text{
text = {"", {"tycoon-passengers-new", insertedPassengerCount}},
position = selectedTrainStation.position,
}
end
for i = 1, #selectedTrainStation.get_inventory(1), 1 do
local p = selectedTrainStation.get_inventory(1)[i]
if p ~= nil and p.valid and p.valid_for_read and p.name == passenger and (p.tags or {}).created == nil then
p.set_tag("created", game.tick)
p.set_tag("origin", string.lower(city.name))
p.set_tag("destination", string.lower(destination))
end
end
end
end
end
end
local function findCityByName(name)
for _, city in ipairs((storage.tycoon_cities or {})) do
if string.lower(city.name) == name then
return city
end
end
return nil
end
local function getCredits(passenger)
local originCity = findCityByName(passenger.origin)
local destinationCity = findCityByName(passenger.destination)
if originCity == nil or destinationCity == nil then
game.print({"", "[color=orange]Factorio Tycoon:[/color] ", "The tycoon mod has encountered a problem: Credits for a passenger couldn't be awarded because the origin or destination city could not be found."})
return 0
end
local isSameSurface = originCity.surface_index == destinationCity.surface_index
local distance = isSameSurface and Util.calculateDistance(originCity.center, destinationCity.center) or 10000
local ticksNeeded = game.tick - passenger.created
-- With vanilla, robot research brings them to 9 tiles per second when level 6 is researched (the first one that needs space science).
local fullRewardForDistance = math.ceil(distance / 100)
local fastestDelivery = distance / 9
local rewardRate = fastestDelivery / ticksNeeded
local reward = fullRewardForDistance * rewardRate
return reward
end
--- @param city City
local function clearPassengers(city)
if not (game.forces.player.technologies["tycoon-public-transportation"] or {}).researched then
return
end
local passengerName = "tycoon-passenger-" .. string.lower(city.name)
local trainStations = Util.list_special_city_buildings(city, "tycoon-passenger-train-station")
if #trainStations > 0 then
for _, trainStation in ipairs(trainStations) do
local cleared = {}
for i = 1, #trainStation.get_inventory(1), 1 do
local p = trainStation.get_inventory(1)[i]
if p ~= nil and p.valid and p.valid_for_read and p.name == passengerName then
table.insert(cleared, {
origin = p.tags.origin,
created = p.tags.created,
destination = p.tags.destination,
})
end
end
local passengersForCurrentCity = trainStation.get_item_count("tycoon-passenger-" .. string.lower(city.name))
if passengersForCurrentCity > 0 then
trainStation.remove_item{name = "tycoon-passenger-" .. string.lower(city.name), count = passengersForCurrentCity}
local reward = 0
for _, v in ipairs(cleared) do
reward = reward + getCredits(v)
end
Consumption.payCurrency(city, reward)
end
storage.tycoon_passenger_transported_count = (storage.tycoon_passenger_transported_count or 0) + #cleared
end
end
end
return {
spawnPassengers = spawnPassengers,
clearPassengers = clearPassengers,
}