Skip to content

Commit ef9132a

Browse files
v2.0.0.4 — Implement full event effect subsystems
- Add utils/EffectHooks.lua: patches EconomyManager.getPricePerLiter and Vehicle.addDamageAmount at load time to apply EVENT_STATE flags as real gameplay modifiers (price multipliers and damage scaling). Global sentinel prevents double-patching. - Convert six boolean EVENT_STATE flags to numeric values across fieldEvents, specialEvents, and animalEvents so they can be used as multipliers: fertilizerBonus/Malus, seedBonus/Malus, harvestBonus/Malus, durabilityBoost/Malus, tradeBonus. - Add four new per-minute tick handlers (60 000 ms stored-timestamp timing): * economicEvents: delivers seed/fertilizer/fuel/equipment discount savings and economic-crisis loan penalties; replaces unreliable modulo timing. * fieldEvents: delivers fertilizer and seed savings proxies as cash. * specialEvents: delivers moneyBonus/Malus cash and XP rep adjustments. * wildlifeEvents: delivers animalProductBonus/Malus scaled by husbandry count. - Festival event now sets marketBonus + moneyBonus (was a pure stub). - Replace equal-chance event selection with proper weighted random selection using the weight field already stored on each event (weight=1 default keeps behaviour identical; infrastructure ready for future tuning). - All tick timestamps (lastEconomicTick, lastFieldTick, lastSpecialTick, lastWildlifeTick) cleared in onEnd to prevent cross-event bleed.
1 parent 1f1ee9b commit ef9132a

8 files changed

Lines changed: 475 additions & 169 deletions

FS25_RandomWorldEvents.zip

2.97 KB
Binary file not shown.

RandomWorldEvents.lua

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -327,8 +327,22 @@ function RandomWorldEvents:triggerRandomEvent()
327327
Logging.info("[RWE] No events available to trigger")
328328
return false
329329
end
330-
331-
local eventId = available[math.random(1, #available)]
330+
331+
-- Weighted random selection: sum weights, pick by accumulated roll
332+
local totalWeight = 0
333+
for _, eid in ipairs(available) do
334+
totalWeight = totalWeight + (self.EVENTS[eid].weight or 1)
335+
end
336+
local roll = math.random() * totalWeight
337+
local cumulative = 0
338+
local eventId = available[#available] -- fallback to last
339+
for _, eid in ipairs(available) do
340+
cumulative = cumulative + (self.EVENTS[eid].weight or 1)
341+
if roll <= cumulative then
342+
eventId = eid
343+
break
344+
end
345+
end
332346
local event = self.EVENTS[eventId]
333347

334348
self.EVENT_STATE.activeEvent = eventId

modDesc.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="utf-8" standalone="no" ?>
22
<modDesc descVersion="92">
33
<author>TisonK</author>
4-
<version>2.0.0.3</version>
4+
<version>2.0.0.4</version>
55
<modName>FS25_RandomWorldEvents</modName>
66
<title>
77
<en>Random World Events</en>
@@ -45,6 +45,7 @@
4545
<extraSourceFiles>
4646
<sourceFile filename="RandomWorldEvents.lua"/>
4747
<sourceFile filename="gui/RWESettingsIntegration.lua"/>
48+
<sourceFile filename="utils/EffectHooks.lua"/>
4849
<sourceFile filename="utils/economicEvents.lua"/>
4950
<sourceFile filename="utils/vehicleEvents.lua"/>
5051
<sourceFile filename="utils/fieldEvents.lua"/>

utils/EffectHooks.lua

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
-- =========================================================
2+
-- Random World Events (version 2.0.0.0) - FS25
3+
-- =========================================================
4+
-- EffectHooks — patches FS25 class methods to apply EVENT_STATE
5+
-- flags as real gameplay modifiers.
6+
--
7+
-- Installed at file-load time. Uses a global sentinel so the
8+
-- file is idempotent even if the engine loads it twice.
9+
-- =========================================================
10+
-- Author: TisonK
11+
-- =========================================================
12+
13+
-- Global sentinel: prevent double-patching.
14+
if _G.RWE_EffectHooks_installed then
15+
Logging.info("[EffectHooks] Already installed, skipping")
16+
return
17+
end
18+
_G.RWE_EffectHooks_installed = true
19+
20+
-- =====================
21+
-- ECONOMY MANAGER HOOK
22+
-- Patches EconomyManager.getPricePerLiter to apply EVENT_STATE
23+
-- price multipliers whenever the player sells goods.
24+
-- =====================
25+
if EconomyManager and EconomyManager.getPricePerLiter then
26+
local origGetPrice = EconomyManager.getPricePerLiter
27+
28+
EconomyManager.getPricePerLiter = function(self, ...)
29+
local price = origGetPrice(self, ...)
30+
if type(price) ~= "number" or price <= 0 then return price end
31+
if not g_RandomWorldEvents then return price end
32+
33+
local s = g_RandomWorldEvents.EVENT_STATE
34+
local mult = 1.0
35+
36+
-- Economic modifiers
37+
if s.marketBonus then mult = mult * (1 + s.marketBonus) end
38+
if s.marketMalus then mult = mult * (1 - s.marketMalus) end
39+
if s.priceFixing then mult = mult * (1 + s.priceFixing) end
40+
if s.exportBonus then mult = mult * (1 + s.exportBonus) end
41+
if s.economicCrisis and s.economicCrisis.marketMalus then
42+
mult = mult * (1 - s.economicCrisis.marketMalus)
43+
end
44+
45+
-- Field modifiers
46+
if s.yieldBonus then mult = mult * (1 + s.yieldBonus) end
47+
if s.yieldMalus then mult = mult * (1 - s.yieldMalus) end
48+
if s.harvestBonus then mult = mult * (1 + s.harvestBonus) end
49+
if s.harvestMalus then mult = mult * (1 - s.harvestMalus) end
50+
if s.fieldSaleBonus then mult = mult * (1 + s.fieldSaleBonus) end
51+
if s.fieldSaleMalus then mult = mult * (1 - s.fieldSaleMalus) end
52+
53+
-- Special / wildlife
54+
if s.tradeBonus then mult = mult * (1 + s.tradeBonus) end
55+
56+
if mult ~= 1.0 and g_RandomWorldEvents.debug and g_RandomWorldEvents.debug.enabled then
57+
Logging.info(string.format(
58+
"[EffectHooks] Price modifier: x%.3f (base €%.2f -> €%.2f)",
59+
mult, price, price * mult))
60+
end
61+
62+
return price * mult
63+
end
64+
65+
Logging.info("[EffectHooks] EconomyManager.getPricePerLiter hooked")
66+
else
67+
Logging.warning("[EffectHooks] EconomyManager.getPricePerLiter not found — price hooks disabled")
68+
end
69+
70+
-- =====================
71+
-- VEHICLE DAMAGE HOOK
72+
-- Patches Vehicle.addDamageAmount to scale incoming damage
73+
-- based on durability EVENT_STATE flags.
74+
-- =====================
75+
if Vehicle and Vehicle.addDamageAmount then
76+
local origAddDamage = Vehicle.addDamageAmount
77+
78+
Vehicle.addDamageAmount = function(self, damage, ...)
79+
if type(damage) ~= "number" or damage <= 0 then
80+
return origAddDamage(self, damage, ...)
81+
end
82+
if not g_RandomWorldEvents then
83+
return origAddDamage(self, damage, ...)
84+
end
85+
86+
local s = g_RandomWorldEvents.EVENT_STATE
87+
local scaledDamage = damage
88+
89+
if s.durabilityBoost then
90+
scaledDamage = scaledDamage * math.max(0, 1 - s.durabilityBoost)
91+
elseif s.durabilityMalus then
92+
scaledDamage = scaledDamage * (1 + s.durabilityMalus)
93+
end
94+
95+
return origAddDamage(self, scaledDamage, ...)
96+
end
97+
98+
Logging.info("[EffectHooks] Vehicle.addDamageAmount hooked")
99+
else
100+
Logging.warning("[EffectHooks] Vehicle.addDamageAmount not found — damage hooks disabled")
101+
end
102+
103+
Logging.info("[EffectHooks] Module loaded successfully")

utils/animalEvents.lua

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,18 @@ animalEvents.eventList = {
4848
end
4949
},
5050

51-
-- 2. Beneficial insects arrive, doubling fertilizer effect -----
51+
-- 2. Beneficial insects arrive, boosting fertilizer effect -----
5252
{
5353
name = "wildlife_beneficial_insects",
5454
minI = 1,
55-
func = function()
55+
func = function(intensity)
5656
if g_RandomWorldEvents then
57-
g_RandomWorldEvents.EVENT_STATE.fertilizerBonus = true
57+
g_RandomWorldEvents.EVENT_STATE.fertilizerBonus = 0.10 + 0.05 * intensity
5858
end
59-
return "Beneficial insects appear! Fertilizer effectiveness doubled!"
59+
return string.format(
60+
"Beneficial insects appear! Fertilizer effectiveness +%.0f%%!",
61+
(0.10 + 0.05 * intensity) * 100
62+
)
6063
end
6164
},
6265

@@ -193,6 +196,48 @@ animalEvents.eventList = {
193196
},
194197
}
195198

199+
-- =====================
200+
-- TICK HANDLER
201+
-- Delivers periodic cash for animal product bonus/malus flags
202+
-- every 60 in-game seconds. numHusbandries scales the amount
203+
-- so farms with more animals feel a bigger effect.
204+
-- =====================
205+
local function wildlifeTickHandler(rwe)
206+
if not g_currentMission then return end
207+
local s = rwe.EVENT_STATE
208+
local t = g_currentMission.time
209+
local lastTick = s.lastWildlifeTick or 0
210+
if t - lastTick < 60000 then return end
211+
s.lastWildlifeTick = t
212+
213+
local farmId = g_currentMission.player and g_currentMission.player.farmId or 0
214+
if farmId == 0 then return end
215+
216+
local numHusbandries = 1
217+
if g_currentMission.animalSystem then
218+
local husbandries = g_currentMission.animalSystem:getHusbandries()
219+
if husbandries then
220+
numHusbandries = math.max(1, #husbandries)
221+
end
222+
end
223+
224+
local amount = 0
225+
if s.animalProductBonus then
226+
local bonus = math.floor(300 * s.animalProductBonus * numHusbandries)
227+
amount = amount + bonus
228+
Logging.info(string.format("[AnimalEvents] Product bonus: +€%d (%d husbandries)", bonus, numHusbandries))
229+
end
230+
if s.animalProductMalus then
231+
local malus = math.floor(250 * s.animalProductMalus * numHusbandries)
232+
amount = amount - malus
233+
Logging.info(string.format("[AnimalEvents] Product malus: -€%d (%d husbandries)", malus, numHusbandries))
234+
end
235+
236+
if amount ~= 0 and g_currentMission.addMoney then
237+
g_currentMission:addMoney(amount, farmId, MoneyType.OTHER, false)
238+
end
239+
end
240+
196241
-- =====================
197242
-- REGISTER WILDLIFE EVENTS
198243
-- =====================
@@ -219,17 +264,20 @@ local function registerAnimalEvents()
219264
-- Clear all possible EVENT_STATE keys set by any wildlife event.
220265
if g_RandomWorldEvents then
221266
local s = g_RandomWorldEvents.EVENT_STATE
222-
s.yieldMalus = nil
223-
s.yieldBonus = nil
224-
s.fertilizerBonus = nil
225-
s.animalProductMalus = nil
226-
s.animalProductBonus = nil
267+
s.yieldMalus = nil
268+
s.yieldBonus = nil
269+
s.fertilizerBonus = nil
270+
s.animalProductMalus = nil
271+
s.animalProductBonus = nil
272+
s.lastWildlifeTick = nil
227273
end
228274
return "Wildlife event ended"
229275
end
230276
})
231277
end
232278

279+
g_RandomWorldEvents:registerTickHandler("wildlifeEvents", wildlifeTickHandler)
280+
233281
Logging.info("[AnimalEvents] Registered " .. #animalEvents.eventList .. " wildlife events")
234282
return true
235283
end

utils/economicEvents.lua

Lines changed: 71 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -153,29 +153,71 @@ economicEvents.eventList = {
153153
-- =====================
154154
-- TICK HANDLER
155155
-- =====================
156-
-- Periodically logs active economic modifiers while an event is running.
157-
-- Registered with the core after events are registered so it runs via
158-
-- RandomWorldEvents:applyActiveEventEffects() — no :update monkey-patching needed.
156+
-- Every 60 in-game seconds: deliver cash savings for active discount
157+
-- events and loan penalties for economic crises. Price-hook-managed
158+
-- flags (marketBonus, priceFixing, exportBonus) are only logged here.
159159
local function economicTickHandler(rwe)
160160
local s = rwe.EVENT_STATE
161161
if not g_currentMission then return end
162162

163-
-- Log active modifiers roughly every 30 in-game seconds (30000 ms).
164163
local t = g_currentMission.time
165-
if t % 30000 < 200 then
166-
if s.priceFixing then
167-
Logging.info("[EconomicEvent] Price fixing active: +%.0f%% sell prices",
168-
s.priceFixing * 100)
169-
end
170-
if s.exportBonus then
171-
Logging.info("[EconomicEvent] Export bonus active: +%.0f%% on exports",
172-
s.exportBonus * 100)
173-
end
174-
if s.economicCrisis then
175-
Logging.info("[EconomicEvent] Economic crisis: Market -%.0f%%, Loans +%.0f%%",
176-
s.economicCrisis.marketMalus * 100,
177-
s.economicCrisis.loanPenalty * 100)
178-
end
164+
local lastTick = s.lastEconomicTick or 0
165+
if t - lastTick < 60000 then return end
166+
s.lastEconomicTick = t
167+
168+
local farmId = g_currentMission.player and g_currentMission.player.farmId or 0
169+
if farmId == 0 then return end
170+
171+
local amount = 0
172+
173+
-- Discount events: delivered as per-minute cash savings proxies
174+
if s.seedDiscount then
175+
local savings = math.floor(500 * s.seedDiscount)
176+
amount = amount + savings
177+
Logging.info(string.format("[EconomicEvents] Seed discount savings: +€%d", savings))
178+
end
179+
if s.fertilizerDiscount then
180+
local savings = math.floor(400 * s.fertilizerDiscount)
181+
amount = amount + savings
182+
Logging.info(string.format("[EconomicEvents] Fertilizer discount savings: +€%d", savings))
183+
end
184+
if s.fuelDiscount then
185+
local savings = math.floor(300 * s.fuelDiscount)
186+
amount = amount + savings
187+
Logging.info(string.format("[EconomicEvents] Fuel discount savings: +€%d", savings))
188+
end
189+
if s.equipmentDiscount then
190+
local savings = math.floor(350 * s.equipmentDiscount)
191+
amount = amount + savings
192+
Logging.info(string.format("[EconomicEvents] Equipment discount savings: +€%d", savings))
193+
end
194+
195+
-- Economic crisis loan penalty
196+
if s.economicCrisis and s.economicCrisis.loanPenalty then
197+
local penalty = math.floor(1000 * s.economicCrisis.loanPenalty)
198+
amount = amount - penalty
199+
Logging.info(string.format("[EconomicEvents] Crisis loan penalty: -€%d", penalty))
200+
end
201+
202+
if amount ~= 0 and g_currentMission.addMoney then
203+
g_currentMission:addMoney(amount, farmId, MoneyType.OTHER, false)
204+
end
205+
206+
-- Log price-hook-managed flags (informational only)
207+
if s.marketBonus then
208+
Logging.info(string.format("[EconomicEvents] Market boom active: +%.0f%% sell prices", s.marketBonus * 100))
209+
end
210+
if s.marketMalus then
211+
Logging.info(string.format("[EconomicEvents] Market crash active: -%.0f%% sell prices", s.marketMalus * 100))
212+
end
213+
if s.priceFixing then
214+
Logging.info(string.format("[EconomicEvents] Price fixing active: +%.0f%% sell prices", s.priceFixing * 100))
215+
end
216+
if s.exportBonus then
217+
Logging.info(string.format("[EconomicEvents] Export bonus active: +%.0f%% on exports", s.exportBonus * 100))
218+
end
219+
if s.economicCrisis and s.economicCrisis.marketMalus then
220+
Logging.info(string.format("[EconomicEvents] Economic crisis: Market -%.0f%%", s.economicCrisis.marketMalus * 100))
179221
end
180222
end
181223

@@ -201,15 +243,17 @@ local function registerEconomicEvents()
201243
onStart = e.func,
202244
onEnd = function()
203245
if g_RandomWorldEvents then
204-
g_RandomWorldEvents.EVENT_STATE.marketBonus = nil
205-
g_RandomWorldEvents.EVENT_STATE.marketMalus = nil
206-
g_RandomWorldEvents.EVENT_STATE.seedDiscount = nil
207-
g_RandomWorldEvents.EVENT_STATE.fertilizerDiscount = nil
208-
g_RandomWorldEvents.EVENT_STATE.fuelDiscount = nil
209-
g_RandomWorldEvents.EVENT_STATE.equipmentDiscount = nil
210-
g_RandomWorldEvents.EVENT_STATE.priceFixing = nil
211-
g_RandomWorldEvents.EVENT_STATE.exportBonus = nil
212-
g_RandomWorldEvents.EVENT_STATE.economicCrisis = nil
246+
local s = g_RandomWorldEvents.EVENT_STATE
247+
s.marketBonus = nil
248+
s.marketMalus = nil
249+
s.seedDiscount = nil
250+
s.fertilizerDiscount = nil
251+
s.fuelDiscount = nil
252+
s.equipmentDiscount = nil
253+
s.priceFixing = nil
254+
s.exportBonus = nil
255+
s.economicCrisis = nil
256+
s.lastEconomicTick = nil
213257
end
214258
return "Economic event ended"
215259
end

0 commit comments

Comments
 (0)