--[[ > Dynamic News by ARS Team, xStream, Red75, Dexxx, Skunk, Xmk > Re-vamped by Alundaio for Call of Chernobyl > Re-vamped by First_Lieutenant_Skelja, senyaGTA, Letozz, Skeli, VanoSanturi, SashaRad, RadioactiveToilet for MLR 8.3 > Re-vamped by Tronex for Call of Chernobyl, Call of Misery, Last Day, Anomaly, Dead Air. Last edit (2018/8/3) --]] ------------------------------------------------------------ -- Control ------------------------------------------------------------ enable_news = true -- useful for pausing news if set to false local TimeFactor = 1 -- don't touch local shw_death_stalker, shw_death_mutant, shw_death_generic, shw_kill_wounded, shw_death_report, shw_found_artifact, shw_heli_call, shw_loot, shw_stash local shw_reaction local shw_weather, shw_time, shw_nearby_activity, shw_zombie local shw_bounty local shw_random_msg, shw_factions_report, shw_zone_activity, shw_found_dead, shw_surge local shw_companions local cycle_TickSpecial, cycle_TickRandom, cycle_TickCompanion, cycle_TickTask = 120,120,120,150 local msg_duration local SOS_offline_time local SOS_offline_period = 60*4 local SOS_warfare_cap_time local SOS_warfare_cap_period = 60*2 local story_LL_trigger, story_MS_trigger = true, true function update_settings() shw_death_stalker = ui_options.get("alife/dynamic_news/death_stalker_news") shw_death_mutant = ui_options.get("alife/dynamic_news/death_mutant_news") shw_death_generic = ui_options.get("alife/dynamic_news/generic_death_news") shw_death_report = ui_options.get("alife/dynamic_news/death_report_news") shw_kill_wounded = ui_options.get("alife/dynamic_news/kill_wounded_news") shw_found_artifact = ui_options.get("alife/dynamic_news/found_artifact_news") shw_heli_call = ui_options.get("alife/dynamic_news/heli_call_news") shw_loot = ui_options.get("alife/dynamic_news/loot_news") shw_stash = false shw_reaction = ui_options.get("alife/dynamic_news/reaction_news") shw_weather = ui_options.get("alife/dynamic_news/weather_news") shw_time = ui_options.get("alife/dynamic_news/time_news") shw_nearby_activity = ui_options.get("alife/dynamic_news/nearby_activity_news") shw_zombie = ui_options.get("alife/dynamic_news/dumb_zombie_news") shw_bounty = ui_options.get("alife/dynamic_news/bounty_news") shw_random_msg = ui_options.get("alife/dynamic_news/random_msg_news") shw_factions_report = ui_options.get("alife/dynamic_news/factions_report_news") shw_zone_activity = ui_options.get("alife/dynamic_news/zone_activity_news") shw_found_dead = ui_options.get("alife/dynamic_news/found_dead_news") shw_surge = ui_options.get("alife/dynamic_news/surge_news") shw_companions = ui_options.get("alife/dynamic_news/companions_news") cycle_TickSpecial = ui_options.get("alife/dynamic_news/cycle_of_special_news") -- Time range: (value + 1) to (value x 2 + 1) cycle_TickTask = ui_options.get("alife/dynamic_news/cycle_of_task_news") -- Time range: (value + 1) to (value x 2 + 1) cycle_TickRandom = ui_options.get("alife/dynamic_news/cycle_of_random_news") -- Time range: (value + 1) to (value x 2 + 1) cycle_TickCompanion = ui_options.get("alife/dynamic_news/cycle_of_companions_news") -- Time range: (value + 1) to (value x 2 + 1) msg_duration = ui_options.get("alife/dynamic_news/message_duration") if (level.present()) then ResetTimeEvent("DynamicNewsManager","TickSpecial",math.random(cycle_TickSpecial + 1, cycle_TickSpecial*2 + 1)) ResetTimeEvent("DynamicNewsManager","TickTask",math.random(cycle_TickTask + 1, cycle_TickTask*2 + 1)) ResetTimeEvent("DynamicNewsManager","TickRandom",math.random(cycle_TickRandom + 1, cycle_TickRandom*2 + 1)) ResetTimeEvent("DynamicNewsManager","TickCompanion",math.random(cycle_TickCompanion + 1, cycle_TickCompanion*2 + 1)) end end ------------------------------------------------------------ -- Preparing / Delay ------------------------------------------------------------ local delay = nil local delay_start = nil local instance = nil -- don't touch function get_dynamic_news() instance = instance or DynamicNewsManager() return instance end function destroy_dynamic_news() if (instance) then instance:destroy() end instance = nil end function actor_on_first_update() delay = delay or time_global() + 7500 end function actor_on_update() if (delay and delay <= time_global()) then if (not delay_start) then delay_start = time_global() + 20000 elseif (delay_start and delay_start <= time_global()) then UnregisterScriptCallback("actor_on_first_update", actor_on_first_update) UnregisterScriptCallback("actor_on_update", actor_on_update) get_dynamic_news() update_settings() -- DRX Questlines trigger, runs once if (not has_alife_info("drx_sl_start_news")) and IsStoryMode() then get_dynamic_news():GossipTaskDRX() db.actor:give_info_portion("drx_sl_start_news") end end end end function on_game_start() destroy_dynamic_news() local function on_game_load() TimeFactor = level.get_time_factor() RegisterScriptCallback("actor_on_first_update", actor_on_first_update) RegisterScriptCallback("actor_on_update", actor_on_update) end RegisterScriptCallback("on_game_load",on_game_load) RegisterScriptCallback("on_option_change",update_settings) end --========================================================================= --///////////////////////////// Dynamic News ////////////////////////////// --========================================================================= class "DynamicNewsManager" function DynamicNewsManager:__init() -- Get player's faction local comm = get_actor_true_community() self.channel_status = { ["general"] = true, ["stalker"] = true, ["monolith"] = (comm == "monolith"), ["csky"] = true, ["army"] = (comm == "army"), ["killer"] = true, ["ecolog"] = true, ["dolg"] = true, ["freedom"] = true, ["bandit"] = true, ["greh"] = (comm == "greh"), ["isg"] = (comm == "isg"), ["renegade"] = true, ["greh_npc"] = true, ["army_npc"] = true, } self.queue = { ["general"] = {}, ["stalker"] = {}, ["monolith"] = {}, ["csky"] = {}, ["army"] = {}, ["killer"] = {}, ["ecolog"] = {}, ["dolg"] = {}, ["freedom"] = {}, ["bandit"] = {}, ["greh"] = {}, ["isg"] = {}, ["renegade"] = {}, ["greh_npc"] = {}, ["army_npc"] = {} } -- serious factions, no spam news self.mono = { ["army_npc"] = true, ["greh_npc"] = true, ["greh"] = true, ["isg"] = true, ["trader"] = true, ["monolith"] = true, } -- mysterious factions, their identity is not known to stalkers self.unknown = { ["isg"] = true, } self.response = { ["type"] = false, ["who"] = false, ["message"] = false } self.spammer = { ["show_about_death"] = 0, ["show_about_kill_wounded"] = 0, ["show_about_death_mutant"] = 0, ["show_about_death_response"] = 0, ["show_about_loot"] = 0 } self.counter = 0 self.max_cnt = 3 -- don't allow more than 3 messages in channels self.loot = {} self.companions_list = dynamic_news_helper.list_actor_squad_by_id() self.news_toggle = xr_conditions.surge_complete() self.surge_shift = not xr_conditions.surge_started() self.surge_type = "" self.sentences_fnames = utils_data.collect_translations("name_stalker_",true) self.sentences_snames = utils_data.collect_translations("lname_private_",true) CreateTimeEvent("DynamicNewsManager","TickNews",10,self.TickNews,self) CreateTimeEvent("DynamicNewsManager","TickQuick",10,self.TickQuick,self) CreateTimeEvent("DynamicNewsManager","TickSpecial",10,self.TickSpecial,self) CreateTimeEvent("DynamicNewsManager","TickTask",10,self.TickTask,self) CreateTimeEvent("DynamicNewsManager","TickRandom",10,self.TickRandom,self) CreateTimeEvent("DynamicNewsManager","TickCompanion",10,self.TickCompanion,self) RegisterScriptCallback("monster_on_death_callback",self) --RegisterScriptCallback("monster_on_net_spawn",self) RegisterScriptCallback("npc_on_death_callback",self) --RegisterScriptCallback("npc_on_hear_callback",self) RegisterScriptCallback("npc_on_get_all_from_corpse",self) end function DynamicNewsManager:destroy() RemoveTimeEvent("DynamicNewsManager","TickNews") RemoveTimeEvent("DynamicNewsManager","TickQuick") RemoveTimeEvent("DynamicNewsManager","TickSpecial") RemoveTimeEvent("DynamicNewsManager","TickTask") RemoveTimeEvent("DynamicNewsManager","TickRandom") RemoveTimeEvent("DynamicNewsManager","TickCompanion") UnregisterScriptCallback("monster_on_death_callback",self) --UnregisterScriptCallback("monster_on_net_spawn",self) UnregisterScriptCallback("npc_on_death_callback",self) --UnregisterScriptCallback("npc_on_hear_callback",self) UnregisterScriptCallback("npc_on_get_all_from_corpse",self) end function DynamicNewsManager:TickNews() if (enable_news == false) then return true end ResetTimeEvent("DynamicNewsManager","TickNews",math.random(5,20)) -- reset the timer, the function will get called again after 5 ~ 20 sec --printf(">>> Dyn News: TickNews call") -- news state check if self:NewsToggle() or (not item_device.is_pda_charged(true)) then return false end -- sending news self.counter = self.counter - 1 if (self.counter < 0) then self.counter = 0 end for ch,messages in pairs(self.queue) do local c = #messages local message = messages[c] if (message) then dynamic_news_helper.send_tip(message.Mg,message.Se, message.Dl, msg_duration,message.Ic,message.Snd,message.It) -- send the news -- Prepare proper reaction if message.Ty then -- if a (type) exists, send reaction message self.response["type"] = message.Ty if message.Id then self.response["who"] = message.Id end ResetTimeEvent("DynamicNewsManager","TickQuick",math.random(5,6)) end messages[c] = nil -- dump the message return false end end return false end function DynamicNewsManager:TickQuick() if (enable_news == false) then return true end ResetTimeEvent("DynamicNewsManager","TickQuick",3600) --printf(">>> Dyn News: TickQuick call") -- news state check if self:NewsToggle() or (not item_device.is_pda_charged(true)) then return false end -- pick news if (self.response["type"] == "artifact") and (self.response["who"]) and (shw_reaction) then if (math.random(100) < 70) then self:ResponseOnFoundArtefact(self.response["who"]) end elseif (self.response["type"] == "trade") and (self.response["who"]) and (shw_reaction) then if (math.random(100) < 70) then self:ResponseOnBoughtItems(self.response["who"]) end elseif (self.response["type"] == "loot") and (self.response["who"]) and (#self.loot > 0) then self:GossipLoot(self.response["who"],self:GetLootBestItem(self.loot),self:GetLootValue(self.loot)) local c = #self.loot if (c > 0) then for i=1,c do self.loot[i] = nil end end elseif (self.response["type"]) and (string.find(self.response["type"], "enemy_activity_")) and (self.response["who"]) and (shw_reaction) then if (math.random(100) < 70) then self:ResponseOnGossipNearbyActivity(self.response["who"],self.response["type"]) end elseif (self.response["type"] == "found_stash") and (shw_reaction) then if (math.random(100) < 60) then self:ResponseOnFoundStash() end elseif (self.response["type"] == "dumb_zombie") and (shw_reaction) then if (math.random(100) < 70) then self:ResponseOnDumbZombie() end elseif (self.response["type"] == "death_by_stalker") and (shw_reaction) then local l = math.random(100) if (l < 45) then -- %45 chance if (self.response["who"]) then self:ResponseOnDeathByStalker(self.response["who"]) end elseif (l > 60) then -- %40 chance self:ResponseOnDeathByStalker_Fake() end elseif (self.response["type"] == "death_by_mutant") and (shw_reaction) then local l = math.random(100) if (l < 45) then if (self.response["who"]) then self:ResponseOnDeathByMutant(self.response["who"]) end elseif (l > 60) then self:ResponseOnDeathByMutant_Fake() end elseif (self.response["type"] == "death_by_surge") and (shw_reaction) then --if math.random(2) == 1 then -- %50 chance --if (self.response["who"]) then --self:ResponseOnDeathBySurges(self.response["who"]) --end --else self:ResponseOnDeathBySurges_Fake() --end end -- send news local message = self.response["message"] if (message) then dynamic_news_helper.send_tip(message.Mg, message.Se, message.To, message.St, message.Ic, message.Snd, message.It) -- (message,name,nil,nil,faction (default),sound,icon (use default icon)) end -- reset self.response["type"] = false self.response["who"] = false self.response["message"] = false return false end function DynamicNewsManager:TickSpecial() if (enable_news == false) then return true end ResetTimeEvent("DynamicNewsManager","TickSpecial",math.random(cycle_TickSpecial + 1, cycle_TickSpecial*2 + 1)) --printf(">>> Dyn News: TickSpecial call - time cycle: " .. cycle_TickSpecial) -- news state check if self:NewsToggle() or (not item_device.is_pda_charged(true)) then return false end -- pick news local pick_tbl = { shw_weather, shw_time, shw_nearby_activity, shw_zombie, shw_broadcast_distress, shw_start_conversation, shw_faction_propaganda, shw_survivor_checkin, shw_contraband_alert, shw_technical_advice, shw_cryptid_sighting, shw_creepypasta_horror } local picked_tbl = {} for i=1,#pick_tbl do if (pick_tbl[i] == true) then table.insert(picked_tbl, i) end end if #picked_tbl == 0 then return false end local pick = picked_tbl[math.random(#picked_tbl)] if (pick == 1) then self:GossipWeather() elseif (pick == 2) then self:GossipTime() elseif (pick == 3) then self:GossipNearbyActivity() elseif (pick == 4) then self:DumbZombie() end return false end function DynamicNewsManager:TickTask() if (enable_news == false) then return true end ResetTimeEvent("DynamicNewsManager","TickTask",math.random(cycle_TickTask + 1, cycle_TickTask*2 + 1)) --printf(">>> Dyn News: TickTask call - time cycle: " .. cycle_TickSpecial) -- news state check if self:NewsToggle() or (not item_device.is_pda_charged(true)) then return false end if (shw_bounty) then if (sim_squad_bounty.check_close_squads()) then self:GossipAlphaSquad() else self:GossipBounty() end end if IsStoryMode() and IsStoryPlayer() then if (not has_alife_info("living_legend")) and story_LL_trigger then self:GossipTaskLL() elseif (not has_alife_info("mortal_sin")) and has_alife_info("living_legend_done") and story_MS_trigger then self:GossipTaskMS() elseif (not has_alife_info("operation_afterglow_info_about_done")) and has_alife_info("mortal_sin_zone_hero") then self:GossipTaskOA() db.actor:give_info_portion("operation_afterglow_info_about_done") elseif (has_alife_info("operation_afterglow_transmission_report")) and (not has_alife_info("lttz_oa_army_degtyarev_jup_meet_msg")) then local Ico = "ui_inGame2_Hero" local Se = game.translate_string("army_degtyarev_jup_name") local player_name = alife():actor():character_name() local msg = game.translate_string("st_lttz_oa_army_degtyarev_jup_meet_msg") msg = strformat(msg, player_name, Se) dynamic_news_helper.send_tip(msg, Se, math.random(10,20), 20, Ico, "news", "npc") db.actor:give_info_portion("lttz_oa_army_degtyarev_jup_meet_msg") elseif (not has_alife_info("unlock_x18")) and has_alife_info("lttz_oa_loose_ends_done") then self:GossipTaskDP() end end return false end function DynamicNewsManager:TickRandom() if (enable_news == false) then return true end ResetTimeEvent("DynamicNewsManager","TickRandom",math.random(cycle_TickRandom + 1, cycle_TickRandom*2 + 1)) --printf(">>> Dyn News: TickRandom call - time cycle: " .. cycle_TickRandom) -- news state check if self:NewsToggle() or (not item_device.is_pda_charged(true)) then return false end -- pick news local pick_tbl = {shw_surge, shw_factions_report, shw_zone_activity, shw_found_dead, shw_random_msg} local picked_tbl = {} for i=1,#pick_tbl do if (pick_tbl[i] == true) then table.insert(picked_tbl, i) end end if #picked_tbl == 0 then return false end local pick = picked_tbl[math.random(#picked_tbl)] if (pick == 1) then self:ReportNextEmission() elseif (pick == 2) then self:ReportByFaction() elseif (pick == 3) then self:ReportZoneActivity() elseif (pick == 4) then self:FoundDead() elseif (pick == 5) then self:SpamRandom() elseif (pick == 6) then end return false end function DynamicNewsManager:TickCompanion() if (enable_news == false) then return true end if not shw_companions then return true end ResetTimeEvent("DynamicNewsManager","TickCompanion",math.random(cycle_TickCompanion + 1, cycle_TickCompanion*2 + 1)) --printf(">>> Dyn News: TickCompanion call - time cycle: " .. cycle_TickCompanion) -- news state check if self:NewsToggle() or (not item_device.is_pda_charged(true)) then return false end -- check new and older companions local is_new, sender, st, npc is_new = true sender = self:PickNewCompanion() if not sender then is_new = false sender = self:PickCompanion() end if (not sender) then return false end -- pick news if is_new then -- new companions news self:CompanionAboutActor(sender) elseif (sender:position():distance_to(db.actor:position()) < 15) and (not sender:best_enemy()) then -- companions news if (math.random(3) == 2) then -- %30 self:CompanionAboutLevel(sender) else self:CompanionAboutLife(sender) end end return false end ------------------------------------------------------------ -- Callbacks ------------------------------------------------------------ function DynamicNewsManager:monster_on_net_spawn(npc,se_obj) end function DynamicNewsManager:monster_on_death_callback(victim,who) --printf(">>> Dyn News: monster_on_death_callback callback") if not (who and IsStalker(who) and shw_death_mutant) then -- if the killer is unknown, go back return end local say = false if (self.spammer.show_about_death_mutant == 0) then local l = math.random(1,2) -- %50 chance if l == 1 then say = self:GossipDeathOfMutant(victim,who) elseif l == 2 then say = self:SeenDeathOfMutant(victim,who) end end if (say) then self.spammer.show_about_death_mutant = self.spammer.show_about_death_mutant + 1 if (self.spammer.show_about_death_mutant > 3) then self.spammer.show_about_death_mutant = 0 end end end function DynamicNewsManager:npc_on_death_callback(victim,who) --printf(">>> Dyn News: npc_on_death_callback callback") if not (db.actor and victim) then return end -- don't show victim info of private faction if actor is not one of them (SOS will be affected) local comm = character_community(victim) if not (self.channel_status[comm]) then return end -- don't show killer info if he's from a mysterious faction if self:IsUnknownCommunity(who) then return end if not (who and who.clsid) then if (shw_death_generic and surge_manager.is_killing_all()) then self:DeathBySurge(victim,who,comm) end return end if (shw_death_report) and (self.spammer.show_about_death_response == 1) then if IsStalker(who) then self:ReportDeathByStalker(victim,who) elseif IsMonster(who) then self:ReportDeathByMutant(victim,who) end end local say = false if (shw_death_stalker) and (self.spammer.show_about_death == 0) then if IsStalker(who) then say = self:SOSDeathByStalker(victim,who,comm) if not (say) then -- if a stalker is NOT reporting on-going attack on him local i = shw_death_generic and math.random(1,3) if i == 1 then self:DeathByStalker(victim,who,comm) else local sender = self:FindSpeaker(victim,who,false,nil,true) if sender then if sender:see(who) or sender:see(victim) then -- actually seeing self:SeenDeathOfStalker(sender,victim,who,comm) else self:GossipDeathByStalker(sender,victim,who) end end end end elseif IsMonster(who) then say = self:SOSDeathByMutant(victim,who,comm) if not (say) then local i = shw_death_generic and math.random(1,2) if i == 1 then self:DeathByMutant(victim,who,comm) else local sender = self:FindSpeaker(victim,who,false,nil,true) if sender and (sender:see(who) or sender:see(victim)) then self:SeenDeathByMutant(sender,victim,who,comm) end end end end end -- reset if (say) then self.spammer.show_about_death = self.spammer.show_about_death + 1 -- 0,1,2,3,4 if (self.spammer.show_about_death > 3) then self.spammer.show_about_death = 0 end end self.spammer.show_about_death_response = self.spammer.show_about_death_response + 1 --0,1,2 if (self.spammer.show_about_death_response > 2) then self.spammer.show_about_death_response = 0 end end function DynamicNewsManager:npc_on_hear_callback(npc,who_id,s_type,sound_dist,sound_power,sound_position) end function DynamicNewsManager:npc_on_get_all_from_corpse(npc,corpse_npc,item,lootable_table) -- The looter, the looted and the loot. --printf(">>> Dyn News: npc_on_get_all_from_corpse callback") if not (shw_loot and db.actor and npc and item) then return end -- using reaction channel cause we want a single loot message per corpse. table.insert(self.loot,item) self.response["who"] = npc self.response["type"] = "loot" ResetTimeEvent("DynamicNewsManager","TickQuick",4) end ------------------------------------------------------------ -- Utilities ------------------------------------------------------------ function DynamicNewsManager:PushToChannel(name,t,fifo) -- (name = faction , t = {message (translated), sender icon, news sound, name and faction of the sender, icon indicator (if its "npc" then use sender's icon), Type of news, Id of sender} , fifo = not used) --printf(">>> Dyn News: PushToChannel call") -- news state check if (not enable_news) or self:NewsToggle() -- necessary to prevent news stacking after surges. or (not item_device.is_pda_charged(true)) or (self.counter > self.max_cnt) then return false end local q = self.queue[name] -- load the faction table in queue table if (q) then if (t.Mg) then for s in string.gmatch(t.Mg,"(st_dyn_news_ch_[%w%d_]*)") do t.Mg = string.gsub(t.Mg,s,game.translate_string(s)) end end if (fifo) then q[#q+1] = t else table.insert(q,1,t) end self.counter = self.counter + 1 end end function DynamicNewsManager:FindSpeakerNoVictim(who,same_as_who,not_in_combat) -- Find a speaker ( [game_object] the guy of interest | [boolean] if true, speaker must be from guy's community | [boolean] if true, speaker must not be in combat ) local comm_sender local comm_who = who and character_community(who) or nil local who_id = who and who:id() or nil local t = {} for i=1, #db.OnlineStalkers do if (who_id == nil) or (db.OnlineStalkers[i] ~= who_id) then local st = db.storage[db.OnlineStalkers[i]] local npc = st and st.object or level.object_by_id(db.OnlineStalkers[i]) if (npc and IsStalker(npc,npc:clsid()) and npc:alive() and not get_object_story_id(db.OnlineStalkers[i])) then if (not_in_combat == nil) or (not_in_combat == true and not npc:best_enemy()) or (not_in_combat ~= true) then comm_sender = npc:character_community() if (same_as_who == nil) or (comm_who == nil) or (same_as_who == true and comm_sender == comm_who) or (same_as_who == false and comm_sender ~= comm_who) then if (self.channel_status[comm_sender]) then t[#t+1] = npc end end end end end end --printf(">>> Dyn News: FindSpeakerNoVictim pass - valid speakers: " .. #t) if (#t == 0) then return nil end return t[math.random(#t)] end function DynamicNewsManager:FindSpeaker(victim,who,same_as_victim,same_as_who,not_in_combat,can_see) -- Find a speaker ( [game_object] the victim | [game_object] the killer | [boolean] if true, speaker must be from victim's community | [boolean] if true, speaker must be from killer's community | [boolean] if true, speaker must not be in combat | [boolean] if true, speaker must see the victim ) local comm = character_community(victim) local comm_sender local comm_who = character_community(who) local who_id = who:id() local t = {} for i=1, #db.OnlineStalkers do if (db.OnlineStalkers[i] ~= who_id) then local st = db.storage[db.OnlineStalkers[i]] local npc = st and st.object if (npc and IsStalker(npc,npc:clsid()) and npc:alive() and not get_object_story_id(db.OnlineStalkers[i])) then if (not_in_combat == nil) or (not_in_combat == true and not npc:best_enemy()) or (not_in_combat ~= true) then comm_sender = npc:character_community() if (same_as_victim == nil) or (same_as_victim == true and comm_sender == comm) or (same_as_victim == false and comm_sender ~= comm) then if (same_as_who == nil) or (same_as_who == true and comm_sender == comm_who) or (same_as_who == false and comm_sender ~= comm_who) then if (self.channel_status[comm_sender]) then if (can_see == nil) or (can_see and npc:see(victim)) or (can_see == false) then t[#t+1] = npc end end end end end end end end --printf(">>> Dyn News: FindSpeaker pass - valid speakers: " .. #t) if (#t == 0) then return nil end return t[math.random(#t)] end function DynamicNewsManager:FindSpeakerWithEnemy(victim,who,same_as_victim,same_as_who,can_see) -- Find a speaker under attack ( [game_object] the victim | [game_object] the killer | [boolean] if true, speaker must be from victim's community | [boolean] if true, speaker must be from killer's community | [boolean] if true, speaker must see the victim ) local comm = character_community(victim) local comm_sender local comm_who = character_community(who) local who_id = who:id() local t = {} for i=1, #db.OnlineStalkers do if (db.OnlineStalkers[i] ~= who_id) then local st = db.storage[db.OnlineStalkers[i]] local npc = st and st.object if (npc and IsStalker(npc,npc:clsid()) and npc:alive() and not get_object_story_id(db.OnlineStalkers[i]) and npc:best_enemy()) then comm_sender = npc:character_community() if (same_as_victim == nil) or (same_as_victim == true and comm_sender == comm) or (same_as_victim == false and comm_sender ~= comm) then if (same_as_who == nil) or (same_as_who == true and comm_sender == comm_who) or (same_as_who == false and comm_sender ~= comm_who) then if (self.channel_status[comm_sender]) then if (can_see == nil) or (can_see and npc:see(victim)) or (can_see == false) then t[#t+1] = npc end end end end end end end --printf(">>> Dyn News: FindSpeakerWithEnemy pass - valid speakers: " .. #t) if (#t == 0) then return nil end return t[math.random(#t)] end function DynamicNewsManager:FindSpeakerRandom(not_in_combat, speaker_community, exclude_npc_id) local t = {} local origin_npc = db.storage[exclude_npc_id] and db.storage[exclude_npc_id].object or level.object_by_id(exclude_npc_id) local origin_npc_name = origin_npc and origin_npc:character_name() or "Unknown" for i=1, #db.OnlineStalkers do local st = db.storage[db.OnlineStalkers[i]] local npc = st and st.object or level.object_by_id(db.OnlineStalkers[i]) if npc and npc:id() ~= exclude_npc_id and not npc:best_enemy() and (speaker_community == nil or npc:character_community() == speaker_community) then if not_in_combat and npc:best_enemy() then -- Skip if the NPC is in combat else t[#t+1] = npc end end end if #t == 0 then return nil, origin_npc_name -- Ensure we return 'Unknown' if no NPC found end return t[math.random(#t)], origin_npc_name -- Correctly return a random NPC and the origin NPC's name end function DynamicNewsManager:FindSpeakerAndTarget(not_in_combat,distance,speaker_community) -- Find a random speaker ([boolean] if true, speaker must not be in combat | [string] if declared, speaker must belong to this community) local sender,target,comm_sender local dis = distance or 300 local t1,t2 = {},{} -- Find a sender for i=1, #db.OnlineStalkers do local st = db.storage[db.OnlineStalkers[i]] local npc = st and st.object or level.object_by_id(db.OnlineStalkers[i]) if (npc and IsStalker(npc,npc:clsid()) and npc:alive() and not get_object_story_id(db.OnlineStalkers[i])) then if (not_in_combat == nil) or (not_in_combat == true and not npc:best_enemy()) or (not_in_combat ~= true) then if (speaker_community == nil) or (speaker_community and npc:character_community() == speaker_community) then comm_sender = npc:character_community() if (self.channel_status[comm_sender]) then t1[#t1+1] = npc end end end end end if (#t1 == 0) then return nil end sender = t1[math.random(#t1)] if (not sender) then return nil end -- Find an enemy to sender local sender_pos = sender:position() local sender_comm = sender:character_community() local sim = alife() local se_obj,clsid for i=1,65534 do se_obj = sim:object(i) clsid = se_obj and se_obj:clsid() if clsid then -- Check if its enemy stalker or monester if ( IsStalker(nil,clsid) and (se_obj:community() ~= "trader") and game_relations.is_factions_enemies(sender_comm, se_obj:community()) ) or (IsMonster(nil,clsid)) then -- Check if its close to sender if (se_obj.position:distance_to(sender_pos) < dis) and (se_obj.group_id ~= 65535) then -- Check if its alive if (se_obj:alive()) then local st = db.storage[se_obj.id] local npc = st and st.object or level.object_by_id(se_obj.id) if npc then t2[#t2+1] = npc end end end end end end if (#t2 == 0) then return nil end target = t2[math.random(#t2)] --printf(">>> Dyn News: FindSpeakerAndTarget pass - valid speakers: " .. #t1 .. " - valid enemies to speaker: " .. #t2) return sender,target end function DynamicNewsManager:FindSpeakerAnywhere(natural_only,faction) -- Find a random speaker ([boolean] if true, speaker must not be in combat | [string] if declared, speaker must belong to this community) local t = {} local size_t = 0 local sim = alife() local act_comm = get_actor_true_community() for i=1,65534 do local se_obj = sim:object(i) if (se_obj and se_obj.group_id ~= 65535) then if IsStalker(nil,se_obj:clsid()) and se_obj:alive() and (se_obj:community() ~= "trader") then local comm = se_obj:community() if faction then if (faction == comm) then size_t = size_t + 1 t[size_t] = i end else if natural_only then if (not game_relations.is_factions_enemies(act_comm, comm)) then size_t = size_t + 1 t[size_t] = i end else size_t = size_t + 1 t[size_t] = i end end end end end return (size_t > 0) and t[math.random(size_t)] or nil end function DynamicNewsManager:IsCommunitySame(npc_1,npc_2) -- Check if both npcs are from the same factions local comm_1 = npc_1:character_community() if (npc_1:id() == AC_ID and comm_1 ~= "actor") then comm_1 = comm_1:sub(7) end local comm_2 = npc_2:character_community() if (npc_2:id() == AC_ID and comm_2 ~= "actor") then comm_2 = comm_2:sub(7) end --printf(">>> Dyn News: IsCommunitySame | npc_1 community = " .. comm_1 .. " | npc_2 community = " .. comm_2) return (comm_1 == comm_2) end function DynamicNewsManager:PickCompanion() -- pick a random companion local npcs = dynamic_news_helper.list_actor_squad_by_id() if #npcs == 0 then return nil end local picked = npcs[math.random(#npcs)] --printf(">>> Dyn News: help PickCompanion | #npcs = " .. #npcs) return db.storage[picked] and db.storage[picked].object end function DynamicNewsManager:PickNewCompanion() -- pick a random "new" companion local new_npcs = {} local tbl_1 = dynamic_news_helper.list_actor_squad_by_id() local tbl_2 = self.companions_list local count = 1 local is_same = false if (#tbl_1 == 0) then return false end -- If there's new companions, put them in (new_npcs) table for i=1,#tbl_1 do if #tbl_2 > 0 then for j=1,#tbl_2 do if tbl_1[i] == tbl_2[j] then is_same = true break end end end if (not is_same) and tbl_1[i] then new_npcs[count] = tbl_1[i] count = count + 1 --printf(">>> Dyn News: help PickNewCompanion - found new companion!") end is_same = false end if (#new_npcs == 0) then return false end -- If there's new companions, update the companions list self.companions_list = dynamic_news_helper.list_actor_squad_by_id() local picked = new_npcs[math.random(#new_npcs)] --printf(">>> Dyn News: help PickNewCompanion | #new_npcs = " .. #new_npcs) return db.storage[picked] and db.storage[picked].object end function DynamicNewsManager:GetLootValue(item_tbl) -- get value of whole loot local value = 0 for i=1,#item_tbl do value = value + item_tbl[i]:cost() end --printf(">>> Dyn News: help GetLootValue | value = " .. value) return value end function DynamicNewsManager:GetLootBestItem(item_tbl) -- get value of best looted item local value = 0 local best_item for i=1,#item_tbl do if item_tbl[i]:cost() > value then value = item_tbl[i]:cost() best_item = item_tbl[i] end end --printf(">>> Dyn News: help GetLootBestItem | best item's value: " .. value) return best_item end function DynamicNewsManager:NewsToggle() -- turn off news if there is an emission, or player is underground --printf(">>> Dyn News: help NewsToggle - self.surge_shift = " .. tostring(self.surge_shift) .. " - self.news_toggle = " .. tostring(self.news_toggle)) -- necessary, otherwise the game will crash due to playing news sound before completely loading. if device():is_paused() then return true end -- disable news if player inside an invalid map. if dynamic_news_helper.IsInvalidMap(level.name()) then return true end -- welcome message on new game, run once if (not has_alife_info("trx_dynamic_news_welcome_to_network")) then self:WelcomeToNetwork() db.actor:give_info_portion("trx_dynamic_news_welcome_to_network") return true end local news_state = true -- if a surge has started, or is underground ---> news go off, erase stored messages, set surge_type if xr_conditions.surge_started() then news_state = false if surge_manager and surge_manager.is_started() then self.surge_type = "emission" elseif psi_storm_manager and psi_storm_manager.is_started() then self.surge_type = "storm" end for ch,messages in pairs(self.queue) do local c = #messages while c > 0 do local message = messages[c] if (message) then messages[c] = nil end c = c - 1 end end end -- if surge started (even on load) ---> set up surge_type, otherwise if surge finished (even on load) ---> send 'surge end' news if self.surge_shift and xr_conditions.surge_started() then self.surge_shift = false elseif (not self.surge_shift) and xr_conditions.surge_complete() then self.surge_shift = true self:GossipEmissionEnd(self.surge_type) self:ReportDeathBySurge() end -- run once at each surge if news_state == (not self.news_toggle) then self.news_toggle = news_state local Msg local num = math.random(5,10) local Se = game.translate_string("st_dyn_news_sender_com_centre") if news_state then Msg = game.translate_string("st_dyn_news_spc_commu_on") dynamic_news_helper.send_tip(Msg,Se,num,msg_duration,"communication","welcome","gr") else Msg = game.translate_string("st_dyn_news_spc_commu_off") dynamic_news_helper.send_tip(Msg,Se,num,msg_duration,"communication","communication_lost","gr") end end return (not news_state) end ------------------------------------------------------------ -- News ------------------------------------------------------------ function DynamicNewsManager:WelcomeToNetwork() -- Welcome message on new game --printf(">>> Dyn News: WelcomeToNetwork - call") local clr_1 = "%" .. "%c[255,160,160,190]" local clr_2 = "%" .. "%c[255,220,220,220]" -- Count the number of stalkers across the zone, except monolith and zombies local c = 1 local sim = alife() for i=1,65534 do local se_obj = sim:object(i) if (se_obj and IsStalker(nil,se_obj:clsid()) and se_obj:alive() and se_obj:community() ~= "zombied" and se_obj:community() ~= "monolith" and se_obj.group_id ~= 65535) then c = c + 1 end end local se_actor = sim:actor() local Se = game.translate_string("st_dyn_news_sender_com_centre") local msg = utils_data.parse_string_keys( game.translate_string("st_dyn_news_welcome_stalker") , {["clr_1"]=clr_1 , ["clr_2"]=clr_2 , ["name"]=se_actor:character_name() , ["number"]=tostring(c)} ) dynamic_news_helper.send_tip(msg,Se, 0, 20,"communication","welcome","gr") -- Reset ResetTimeEvent("DynamicNewsManager","TickSpecial",math.random(cycle_TickSpecial + 1, cycle_TickSpecial*2 + 1)) ResetTimeEvent("DynamicNewsManager","TickTask",math.random(cycle_TickTask + 1, cycle_TickTask*2 + 1)) ResetTimeEvent("DynamicNewsManager","TickRandom",math.random(cycle_TickRandom + 1, cycle_TickRandom*2 + 1)) ResetTimeEvent("DynamicNewsManager","TickCompanion",math.random(cycle_TickCompanion + 1, cycle_TickCompanion*2 + 1)) end --< Report News >-- function DynamicNewsManager:DeathBySurge(victim,who,comm) -- Death by blowouts reports --printf(">>> Dyn News: DeathBySurge - call") local msg = strformat("%c[255,160,160,160]%s, %s.\\n%c[default]st_dyn_news_ch_found %s. st_dyn_news_ch_blowout.",victim:character_name(),game.translate_string(comm),dynamic_news_helper.GetPointDescription(victim)) self:PushToChannel("general",{Mg=msg,Ic="deth",Snd="news",Se=game.translate_string("st_dyn_news_ch_died"),It="gr",Ty="death_by_surge",Id=victim}) return true end function DynamicNewsManager:ReportDeathBySurge() -- Report casualties of surges (fake) --printf(">>> Dyn News: ReportDeathBySurge - call") if (not shw_death_report) and ((not self.sentences_fnames) or (not self.sentences_fnames)) then return false end local clr_1 = "%c[255,160,160,190]" local clr_2 = "%c[255,220,220,220]" local known_num = math.random (1,6) local msg = clr_1 .. game.translate_string("st_dyn_news_death_by_surge_start") .. clr_2 local finish = utils_data.parse_string_keys( game.translate_string("st_dyn_news_death_by_surge_end") , { ["num"] = tostring(math.random(3,16)) } ) local a,b,c,y1,y2,z1,z2,z3 for i=1,known_num do a = self.sentences_fnames[math.random(#self.sentences_fnames)] b = self.sentences_snames[math.random(#self.sentences_snames)] c = dynamic_news_helper.PickMap(level.name()) c = game.translate_string(c) y1 = string.char(math.random(65,90)) y2 = string.char(math.random(65,90)) z1 = tostring(math.random(9)) z2 = tostring(math.random(9)) z3 = tostring(math.random(9)) msg = msg .. strformat("\\n%s-%s %s%s-%s%s%s, %s %s, %s.",clr_1,clr_2,y1,y2,z1,z2,z3,a,b,c) end local Se = game.translate_string("st_dyn_news_sender_obituary") msg = msg .. "\\n" .. clr_1 .. finish --dynamic_news_helper.send_tip(msg,Se,30,msg_duration,"death","beep_2","gr") self:PushToChannel("general",{Mg=msg,Ic="death",Snd="beep_2",Se=Se,It="gr",Ty="death_by_surge",Dl=30}) -- special case for response message self.response["type"] = "death_by_surge" ResetTimeEvent("DynamicNewsManager","TickQuick",math.random(5,6)) return true end function DynamicNewsManager:DeathByStalker(victim,who,comm) -- Found dead stalker by stalker --printf(">>> Dyn News: DeathByStalker - call") local cls = dynamic_news_helper.GetWeaponClass(who) if (cls == 1 or cls == 9) then return false end local msg = strformat("%c[255,160,160,160]%s, %s.\\n%c[default]st_dyn_news_ch_found %s. %s.",victim:character_name(),game.translate_string(comm),dynamic_news_helper.GetPointDescription(victim),dynamic_news_helper.GetWeaponDescription(who,1)) self:PushToChannel("general",{Mg=msg,Ic="deth",Snd="news",Se=game.translate_string("st_dyn_news_ch_died"),It="gr",Ty="death_by_stalker",Id=victim}) return true end function DynamicNewsManager:ReportDeathByStalker(victim,who) -- Death Report of stalker killing stalker --printf(">>> Dyn News: ReportDeathByStalker - call") if self:IsCommunitySame(victim,who) then return false end if self:IsUnknownCommunity(who) or self:IsUnknownCommunity(victim) then return false end local clr_1 = "%" .. "%c[255,160,160,190]" local clr_2 = "%" .. "%c[255,220,220,220]" local a = game.translate_string(who:character_community()) local b = game.translate_string(victim:character_community()) local c = dynamic_news_helper.GetPointDescription(victim) local who_name = who:character_name() if (who:id() == AC_ID) then local sim = alife() local se_actor = sim:actor() who_name = se_actor:character_name() end local Se = game.translate_string("st_dyn_news_sender_obituary") local msg = utils_data.parse_string_keys( game.translate_string("st_dyn_news_spc_obituary_s") , {["clr_1"]=clr_1 , ["clr_2"]=clr_2 , ["who_name"]=who_name , ["who_comm"]=a , ["victim_name"]=victim:character_name() , ["victim_comm"]=b , ["where"]=c} ) self:PushToChannel("general",{Mg=msg,Ic="death",Snd="no_sound",Se=Se,It="gr",Ty="death_by_stalker",Id=victim}) return true end function DynamicNewsManager:DeathByMutant(victim,who,comm) -- Found dead stalker by mutant --printf(">>> Dyn News: DeathByMutant - call") local a = victim:character_name() local b = game.translate_string(comm) local c = dynamic_news_helper.GetPointDescription(victim) local d if (ini_sys:r_string_ex("string_table","language") == "rus") then d = dynamic_news_helper.GetMonsterDescription(who,7) else d = string.gsub(dynamic_news_helper.GetMonsterDescription(who,1),"(%l)?",string.upper("%1"),1) end local msg = strformat("%c[255,160,160,160]%s, %s.\\n%c[default]st_dyn_news_ch_found %s. %s.",a,b,c,d) self:PushToChannel("general",{Mg=msg,Ic="deth",Snd="news",Se=game.translate_string("st_dyn_news_ch_died"),It="gr",Ty="death_by_mutant",Id=victim}) return true end function DynamicNewsManager:ReportDeathByMutant(victim,who) -- Death Report by mutant killing stalker --printf(">>> Dyn News: ReportDeathByMutant - call") local clr_1 = "%" .. "%c[255,160,160,190]" local clr_2 = "%" .. "%c[255,220,220,220]" local a = string.gsub(dynamic_news_helper.GetMonsterDescription(who,1),"(%l)?",string.upper("%1"),1) local b = game.translate_string(victim:character_community()) local c = dynamic_news_helper.GetPointDescription(victim) local Se = game.translate_string("st_dyn_news_sender_obituary") local msg = utils_data.parse_string_keys( game.translate_string("st_dyn_news_spc_obituary_m") , {["clr_1"]=clr_1 , ["clr_2"]=clr_2 , ["victim_name"]=victim:character_name() , ["victim_comm"]=b , ["who"]=a , ["where"]=c} ) self:PushToChannel("general",{Mg=msg,Ic="death",Snd="no_sound",Se=Se,It="gr",Ty="death_by_mutant",Id=victim}) return true end function DynamicNewsManager:GossipDeathByStalker(sender,victim,who) -- hearing stalker killing a stalker --printf(">>> Dyn News: GossipDeathByStalker - call") local cls = dynamic_news_helper.GetWeaponClass(who) if (cls == 1 or cls == 9) then return false end if self:IsCommunitySame(victim,who) then return false end local sender = sender or self:FindSpeaker(victim,who,false,nil,true) if self:IsSpecialNPC(sender) then return false end local tbl_1 = utils_data.collect_translations("st_dyn_news_builder_hear_",true) local tbl_2 = utils_data.collect_translations("st_dyn_news_builder_ending_",true) if (not tbl_1) or (not tbl_2) then return false end local a = tbl_1[math.random(#tbl_1)] local b = dynamic_news_helper.GetWeaponDescription(who,2) local c = dynamic_news_helper.GetPointDescription(victim) local d = tbl_2[math.random(#tbl_2)] local Se = strformat("%s, %s",sender:character_name(),dynamic_news_helper.GetCommunityDescription(sender,6)) local msg = strformat("%s %s %s. %s",a,b,c,d) self:PushToChannel(sender:character_community(),{Mg=msg,Ic=sender:character_icon(),Snd="news",Se=Se,It="npc"}) return true end function DynamicNewsManager:SOSDeathByStalker(victim,who,comm) -- stalker under stalkers attack --printf(">>> Dyn News: SOSDeathByStalker - call") local cls = dynamic_news_helper.GetWeaponClass(who) if (cls == 1 or cls == 9) then return false end if self:IsCommunitySame(victim,who) then return false end local sender = self:FindSpeakerWithEnemy(victim,who,true,false) if self:IsSpecialNPC(sender) then return false end local a = dynamic_news_helper.GetCommunityDescription(sender,math.random(11,14)) local b,c = self:BuildSentenceStalkerEnemy(victim,who,0,30) local d = dynamic_news_helper.GetPointDescription(victim) local Se = strformat("%s, %s",sender:character_name(),dynamic_news_helper.GetCommunityDescription(sender,6)) local msg = strformat("%s %s %s %s!",a,b,c,d) self:PushToChannel(sender:character_community(),{Mg=msg,Ic=sender:character_icon(),Snd="danger",Se=Se,It="npc"}) return true end function DynamicNewsManager:SeenDeathOfStalker(sender,victim,who,comm) -- seeing stalker killing a stalker --printf(">>> Dyn News: SeenDeathOfStalker - call") local cls = dynamic_news_helper.GetWeaponClass(who) if (cls == 1 or cls == 9) then return false end if self:IsCommunitySame(victim,who) then return false end local sender = sender or self:FindSpeaker(victim,who,false,false,true) if self:IsSpecialNPC(sender) then return false end local tbl = utils_data.collect_translations("st_dyn_news_builder_sight_",true) if (not tbl) then return false end local a = tbl[math.random(#tbl)] local b = dynamic_news_helper.GetCommunityDescription(who,math.random(1,2)) local c = dynamic_news_helper.GetWeaponDescription(who,math.random(4,8)) local d = dynamic_news_helper.GetCommunityDescription(victim,math.random(3,4)) local e = dynamic_news_helper.GetPointDescription(victim) local Se = strformat("%s, %s",sender:character_name(),dynamic_news_helper.GetCommunityDescription(sender,6)) local msg = strformat("%s %s %s %s %s.",a,b,c,d,e) self:PushToChannel("general",{Mg=msg,Ic=sender:character_icon(),Snd="danger",Se=Se,It="npc"}) return true end function DynamicNewsManager:SeenDeathOfMutant(victim,who) -- seeing stalker killing a mutant --printf(">>> Dyn News: SeenDeathOfMutant - call") local cls = dynamic_news_helper.GetWeaponClass(who) if (cls == 1 or cls == 9) then return false end local sender = self:FindSpeaker(victim,who,nil,false,true) if self:IsSpecialNPC(sender) then return false end local tbl = utils_data.collect_translations("st_dyn_news_builder_sight_",true) if (not tbl) then return false end local a = tbl[math.random(#tbl)] local b = dynamic_news_helper.GetCommunityDescription(who,math.random(1,2)) local c = dynamic_news_helper.GetWeaponDescription(who,math.random(4,6)) local d = dynamic_news_helper.GetMonsterDescription(victim,1) local e = dynamic_news_helper.GetPointDescription(victim) local Se = strformat("%s, %s",sender:character_name(),dynamic_news_helper.GetCommunityDescription(sender,6)) local msg = strformat("%s %s %s %s %s.",a,b,c,d,e) -- example "I just saw" "a loner" "shots" "a bloodsucker" "north east of road to rostok" self:PushToChannel(sender:character_community(),{Mg=msg,Ic=sender:character_icon(),Snd="danger",Se=Se,It="npc"}) return true end function DynamicNewsManager:SeenDeathByMutant(sender,victim,who,comm) -- seeing stalker killed by a mutant --printf(">>> Dyn News: SeenDeathByMutant - call") local sender = sender or self:FindSpeaker(victim,who,false,nil,true) if self:IsSpecialNPC(sender) then return false end local tbl_1 = utils_data.collect_translations("st_dyn_news_builder_hear_",true) -- was sight local tbl_2 = utils_data.collect_translations("st_dyn_news_builder_middle_",true) local tbl_3 = utils_data.collect_translations("st_dyn_news_builder_ending_",true) if (not tbl_1) or (not tbl_2) or (not tbl_3) then return false end local a = tbl_1[math.random(#tbl_1)] local b = tbl_2[math.random(#tbl_2)] local c = dynamic_news_helper.GetPointDescription(victim) local d = tbl_3[math.random(#tbl_3)] local Se = strformat("%s, %s",sender:character_name(),dynamic_news_helper.GetCommunityDescription(sender,6)) local msg = strformat("%s %s %s.\\n%s",a,b,c,d) self:PushToChannel(sender:character_community(),{Mg=msg,Ic=sender:character_icon(),Snd="danger",Se=Se,It="npc"}) return true end function DynamicNewsManager:GossipDeathOfMutant(victim,who,comm) -- hearing stalker killing a mutant --printf(">>> Dyn News: GossipDeathOfMutant - call") local cls = dynamic_news_helper.GetWeaponClass(who) if (cls == 1 or cls == 9) then return false end local sender = self:FindSpeaker(victim,who,nil,false,true) if self:IsSpecialNPC(sender) then return false end local tbl_1 = utils_data.collect_translations("st_dyn_news_builder_hear_",true) local tbl_2 = utils_data.collect_translations("st_dyn_news_builder_ending_",true) if (not tbl_1) or (not tbl_2) then return false end local a = tbl_1[math.random(#tbl_1)] local b = dynamic_news_helper.GetWeaponDescription(who,2) local c = dynamic_news_helper.GetPointDescription(victim) local d = tbl_2[math.random(#tbl_2)] local Se = strformat("%s, %s",sender:character_name(),dynamic_news_helper.GetCommunityDescription(sender,6)) local msg = strformat("%s %s %s. %s",a,b,c,d) self:PushToChannel(sender:character_community(),{Mg=msg,Ic=sender:character_icon(),Snd="news",Se=Se,It="npc"}) return true end function DynamicNewsManager:SOSDeathByMutant(victim,who) -- stalker under mutants attack --printf(">>> Dyn News: SOSDeathByMutant - call") local sender = self:FindSpeakerWithEnemy(victim,who,true,nil) if self:IsSpecialNPC(sender) then return false end local a = dynamic_news_helper.GetCommunityDescription(sender,math.random(11,14)) local b = dynamic_news_helper.GetMonsterDescription(who,math.random(2,5)) local c = dynamic_news_helper.GetPointDescription(victim) local Se = strformat("%s, %s",sender:character_name(),dynamic_news_helper.GetCommunityDescription(sender,6)) local msg = strformat("%s %s %s!",a,b,c) self:PushToChannel(sender:character_community(),{Mg=msg,Ic=sender:character_icon(),Snd="danger",Se=Se,It="npc"}) return true end function DynamicNewsManager:SOSBattleOffline(sq_v,sq_w) -- stalker under mutants/stalker attack in other maps --printf(">>> Dyn News: SOSBattleOffline - call") if (not shw_death_stalker) then return end if self:NewsToggle() or (not item_device.is_pda_charged(true)) then -- necessary to prevent news stacking after surges. return end if (not sq_v) or (not sq_w) then return end -- To prevent news spam local curr_time = game.get_game_time() if SOS_offline_time and (curr_time:diffSec(SOS_offline_time) < (SOS_offline_period * TimeFactor)) then return end SOS_offline_time = curr_time if (not enable_news) then return end local members_v, members_w = {},{} for k in sq_v:squad_members() do members_v[#members_v + 1] = k.id end for k in sq_w:squad_members() do members_w[#members_w + 1] = k.id end if (#members_w == 0) or (#members_v == 0) then return end local comm_v = sq_v:get_squad_community() local comm_w = sq_w:get_squad_community() local se_v = alife_object(members_v[math.random(#members_v)]) local se_w = alife_object(members_w[math.random(#members_w)]) local cls_v = se_v:clsid() local cls_w = se_w:clsid() if IsMonster(nil,cls_v) or (not (self.channel_status[comm_v])) then return end if self.unknown[comm_w] then return end local comm_desc = game.translate_string("st_dyn_news_comm_" .. comm_v .. "_" .. tostring(6)) local a = game.translate_string("st_dyn_news_comm_" .. comm_v .. "_" .. tostring(math.random(11,14))) local c = dynamic_news_helper.GetPointDescription(se_v) local b if IsMonster(nil,cls_w) then b = dynamic_news_helper.GetMonsterDescription(nil,math.random(2,5),se_w:section_name(),cls_w) else local b1,b2 = self:BuildSentenceStalkerEnemy_Offline(comm_w) b = strformat("%s %s",b1,b2) end local Se = strformat("%s, %s",se_v:character_name(),comm_desc) local msg = strformat("%s %s %s!",a,b,c) --dynamic_news_helper.send_tip(msg,Se, nil, msg_duration,comm_v,"beep_1","gr") self:PushToChannel(comm_v, {Mg=msg,Ic=comm_v,Snd="beep_1",Se=Se,It="gr"}) end function DynamicNewsManager:SOSWarfareCapture(sq) -- stalker under mutants/stalker attack in other maps --printf(">>> Dyn News: SOSBattleOffline - call") if self:NewsToggle() or (not item_device.is_pda_charged(true)) then -- necessary to prevent news stacking after surges. return end if (not sq) then return end -- To prevent news spam local curr_time = game.get_game_time() if SOS_warfare_cap_time and (curr_time:diffSec(SOS_warfare_cap_time) < (SOS_warfare_cap_period * TimeFactor)) then return end SOS_warfare_cap_time = curr_time local members = {} for k in sq:squad_members() do members[#members + 1] = k.id end if (#members == 0)then return end local comm = sq:get_squad_community() local se_npc = alife_object(members[math.random(#members)]) local cls = se_npc:clsid() local sender_name = se_npc:character_name() local sender_comm = game.translate_string("st_dyn_news_comm_" .. comm .. "_" .. tostring(6)) local fac_profile = dynamic_news_helper.GetFaction(sender_comm) if (not fac_profile) then return false end local sender_type = fac_profile["type"] local location = dynamic_news_helper.GetPointDescription(se_npc) local lvl = alife():level_name(game_graph():vertex(se_npc.m_game_vertex_id):level_id()) local level_name = game.translate_string(lvl) local tbl = utils_data.collect_translations("st_dyn_news_warfare_capture_" .. sender_type .. "_", true) if (not tbl) then return false end local Se = strformat("%s, %s",sender_name,sender_comm) local msg = utils_data.parse_string_keys( tbl[math.random(#tbl)] , { ["speaker"]=sender_name , ["map"]=level_name , ["location"]=location } ) --dynamic_news_helper.send_tip(msg,Se, nil, msg_duration,comm,"beep_1","gr") self:PushToChannel(comm, {Mg=msg,Ic=comm,Snd="beep_1",Se=Se,It="gr"}) end function DynamicNewsManager:KillWounded(sender,victim,is_hostage) -- killing a wounded stalker --printf(">>> Dyn News: KillWounded - call") if not (shw_kill_wounded) then return false end local squad = get_object_squad(sender) if not (squad and squad:commander_id() == sender:id()) then return false end if (self.spammer.show_about_kill_wounded == 0) then local tbl if not (is_hostage) then tbl = utils_data.collect_translations("st_dyn_news_gossip_kill_wounded_",true) else tbl = utils_data.collect_translations("st_dyn_news_gossip_hostage_",true) end if (not tbl) then return false end local Se = strformat("%s, %s",sender:character_name(),dynamic_news_helper.GetCommunityDescription(sender,6)) self:PushToChannel(sender:character_community(),{Mg=tbl[math.random(#tbl)],Ic=sender:character_icon(),Snd="news",Se=Se,It="npc"}) return true end self.spammer.show_about_kill_wounded = self.spammer.show_about_kill_wounded + 1 if (self.spammer.show_about_kill_wounded > 10) then self.spammer.show_about_kill_wounded = 0 end end function DynamicNewsManager:RadioInHeli(sender,who) -- requesting chopper assistance --printf(">>> Dyn News: RadioInHeli - call") if (not shw_heli_call) then return false end if self:IsSpecialNPC(sender) then return false end local a = dynamic_news_helper.GetPointDescription(who) local b = IsStalker(who) and dynamic_news_helper.GetCommunityDescription(who,math.random(1,2)) or dynamic_news_helper.GetMonsterDescription(who,6) local tbl = utils_data.collect_translations("st_dyn_news_gossip_heli_",true) if (not tbl) then return false end local Se = strformat("%s, %s",sender:character_name(),dynamic_news_helper.GetCommunityDescription(sender,6)) local msg = utils_data.parse_string_keys( tbl[math.random(#tbl)] , { ["where"]=a , ["what"]=b } ) self:PushToChannel(sender:character_community(),{Mg=msg,Ic=sender:character_icon(),Snd="news",Se=Se,It="npc"}) return true end function DynamicNewsManager:FoundArtefact(sender,itm) -- stalker found an artifact --printf(">>> Dyn News: FoundArtefact - call") if (not shw_found_artifact) then return false end if self:IsSpecialNPC(sender) or self:IsMonoCommunity(sender) then return false end local inv_name = ui_item.get_sec_name(itm:section()) local tbl = utils_data.collect_translations("st_dyn_news_gossip_arte_",true) if (not tbl) then return false end local Se = strformat("%s, %s",sender:character_name(),dynamic_news_helper.GetCommunityDescription(sender,6)) local msg = utils_data.parse_string_keys( tbl[math.random(#tbl)] , { ["what"]=inv_name } ) self:PushToChannel(sender:character_community(),{Mg=msg,Ic=sender:character_icon(),Snd="news",Se=Se,It="npc",Ty="artifact", Id=sender}) return true end function DynamicNewsManager:FoundStash() -- reaction of angry stash owners --printf(">>> Dyn News: FoundStash - call") if not (shw_stash and self.sentences_fnames and self.sentences_snames) then return false end if (math.random(100) < 40) then -- %60 chance to ignore return false end local tbl = utils_data.collect_translations("st_dyn_news_found_stash_",true) if (not tbl) then return false end local Se = strformat("%s %s" , self.sentences_fnames[math.random(#self.sentences_fnames)] , self.sentences_snames[math.random(#self.sentences_snames)]) self:PushToChannel("general",{Mg=tbl[math.random(#tbl)],Ic="common",Snd="beep_2",Se=Se,It="gr",Ty="found_stash"}) return true end function DynamicNewsManager:BoughtItems(sender,who,list) -- trading --printf(">>> Dyn News: BoughtItems - call") local sec = list[math.random(#list)] local inv_name = ui_item.get_sec_name(sec) local tbl = utils_data.collect_translations("st_dyn_news_gossip_buy_",true) if (not tbl) then return false end inv_name = ui_item.get_plural_name(inv_name) local Se = strformat("%s, %s",sender:character_name(),dynamic_news_helper.GetCommunityDescription(sender,6)) local msg = utils_data.parse_string_keys( tbl[math.random(#tbl)] , { ["who"]=who:character_name() , ["what"]=inv_name } ) self:PushToChannel(sender:character_community(),{Mg=msg,Ic=sender:character_icon(),Snd="news",Se=Se,It="npc",Ty="trade", Id=sender}) return true end function DynamicNewsManager:UpgradedItems(sender,who,wpn_sec,list) -- upgrading --printf(">>> Dyn News: UpgradedItems - call") local upgrade = list[math.random(#list)] local upg_str = upgrade and ini_sys:r_string_ex(upgrade,"name") local upg_name = upg_str and game.translate_string(upg_str) local inv_name = wpn_sec and ui_item.get_sec_name(wpn_sec) if not (upg_name and inv_name) then return false end local tbl = utils_data.collect_translations("st_dyn_news_gossip_upgrade_",true) if (not tbl) then return false end local Se = strformat("%s, %s",sender:character_name(),dynamic_news_helper.GetCommunityDescription(sender,6)) local msg = utils_data.parse_string_keys( tbl[math.random(#tbl)] , { ["who"]=who:character_name() , ["what"]=inv_name , ["upgrade"]=upg_name } ) self:PushToChannel(sender:character_community(),{Mg=msg,Ic=sender:character_icon(),Snd="news",Se=Se,It="npc",Ty="upgrade", Id=sender}) return true end function DynamicNewsManager:GossipLoot(sender, best_item, all_value) --printf(">>> Dyn News: GossipLoot - call") if not (sender and best_item and all_value) then return false end if (sender:character_community() == "monolith") then return false end -- Limit the spam self.spammer.show_about_loot = self.spammer.show_about_loot + 1 if (self.spammer.show_about_loot > 1) then self.spammer.show_about_loot = 0 end if (self.spammer.show_about_loot == 0) then return false end local tbl, msg if (math.random(2) == 1) then if all_value >= 1200 then tbl = utils_data.collect_translations("st_dyn_news_loot_good_", true) else tbl = utils_data.collect_translations("st_dyn_news_loot_bad_", true) end if (not tbl) then return false end msg = tbl[math.random(#tbl)] elseif best_item then -- Get the item's display name using get_sec_name local item_name = ui_item.get_sec_name(best_item:section()) local tbl = utils_data.collect_translations("st_dyn_news_loot_item_", true) if (not tbl) then return false end msg = utils_data.parse_string_keys(tbl[math.random(#tbl)], { ["what"] = item_name }) end local Se = string.format("%s, %s", sender:character_name(), dynamic_news_helper.GetCommunityDescription(sender, 6)) -- Push the message to the appropriate channel self:PushToChannel(sender:character_community(), { Mg = msg, Ic = sender:character_icon(), Snd = "beep_1", Se = Se, It = "npc" }) return true end --< Random News >-- function DynamicNewsManager:ReportNextEmission() -- news about expected surge's date --printf(">>> Dyn News: ReportNextEmission - call") local SM, SurgeType, last_surge_time, AccuracyTier, comm, Se, clr, msg, a, b, c, d, Tbl_msg local IsEmission, IsPsiStorm = false, false -- %50 chance to pick blowout or psi-storm if (math.random(2) == 1) then SurgeType = game.translate_string("st_dyn_news_surge_type_emission") SM = surge_manager and surge_manager.SurgeManager if (ui_options.get("alife/event/emission_state")) and (SM ~= nil and SM._delta ~= nil and not SM.started) then -- if blowouts are enabled last_surge_time = SM and SM.last_surge_time or game.get_game_time() IsEmission = true end else SurgeType = game.translate_string("st_dyn_news_surge_type_psi") SM = psi_storm_manager and psi_storm_manager.PsiStormManager if (ui_options.get("alife/event/psi_storm_state")) and (SM ~= nil and SM._delta ~= nil and not SM.started) then last_surge_time = SM and SM.last_psi_storm_time or game.get_game_time() IsPsiStorm = true end end -- preparing the news if IsEmission or IsPsiStorm then local g_time = game.get_game_time() local surge_start = SM and math.floor(SM._delta - g_time:diffSec(last_surge_time)) -- time "till" next blowout (in second) = ( time of next surge[SCOPED] - (current time - last surge time)[SCOPED] ) -- %50 chance to pick News from people or natural faction if math.random(2) == 1 then if (not self.sentences_fnames) or (not self.sentences_fnames) then return false end comm = "common" Se = strformat("%s %s", self.sentences_fnames[math.random(#self.sentences_fnames)] , self.sentences_snames[math.random(#self.sentences_snames)] ) Tbl_msg = utils_data.collect_translations("st_dyn_news_surge_template_",true) if (not Tbl_msg) then return false end a = Tbl_msg[math.random(#Tbl_msg)] b = dynamic_news_helper.GetTimeString(math.floor(surge_start/3600), math.random(4)) if (a and b and SurgeType) then msg = utils_data.parse_string_keys( a , {["what"]=SurgeType , ["when"]=b} ) end else comm = dynamic_news_helper.PickFaction(true) if (not comm) then return false end Se = strformat("%s, %s" , game.translate_string("st_faction_" .. comm) , game.translate_string("st_dyn_news_sender_private_ch")) if ((comm == "ecolog") or (comm == "csky") or (comm == "dolg") or (comm == "army")) then AccuracyTier = 5 else AccuracyTier = math.random(5) end Tbl_msg = utils_data.collect_translations("st_dyn_news_surge_builder_start_" .. comm .. "_",true) if (not Tbl_msg) then return false end a = Tbl_msg[math.random(#Tbl_msg)] Tbl_msg = utils_data.collect_translations("st_dyn_news_surge_builder_mid_",true) if (not Tbl_msg) then return false end b = Tbl_msg[math.random(#Tbl_msg)] Tbl_msg = utils_data.collect_translations("st_dyn_news_surge_builder_end_" .. comm .. "_",true) if (not Tbl_msg) then return false end d = Tbl_msg[math.random(#Tbl_msg)] c = dynamic_news_helper.GetTimeString(math.floor(surge_start/3600), AccuracyTier) if (a and b and c and d and SurgeType) then local msg_mid = utils_data.parse_string_keys( b , {["what"]=SurgeType , ["when"]=c} ) msg = utils_data.parse_string_keys( "$start $mid. $end." , {["start"]=a , ["mid"]=msg_mid , ["end"]=d} ) end end end if not (msg and comm and Se) then return false end --dynamic_news_helper.send_tip(msg,Se, nil, msg_duration,comm,"beep_1","gr") self:PushToChannel("general",{Mg=msg,Ic=comm,Snd="beep_1",Se=Se,It="gr"}) return true end function DynamicNewsManager:ReportByFaction() -- faction's news --printf(">>> Dyn News: ReportByFaction - call") local comm = dynamic_news_helper.PickFaction() local Se = game.translate_string("st_faction_" .. comm) local tbl = utils_data.collect_translations("st_dyn_news_spam_faction_" .. comm .. "_",true) if (not tbl) then return false end local msg = tbl[math.random(#tbl)] --dynamic_news_helper.send_tip(tbl[math.random(#tbl)],Se, nil, msg_duration,comm,"beep_1","gr") self:PushToChannel(comm, {Mg=msg,Ic=comm,Snd="beep_1",Se=Se,It="gr"}) return true end function DynamicNewsManager:ReportZoneActivity() -- report activity of squads in other maps local stalkers = {} -- id local enemies = {} -- community local naturals = {} -- community local monsters = {} -- clsid local sim = alife() local gg = game_graph() local sfind = string.find local monster_tiers = faction_expansions.mutant_tier_by_clsid local lvl_name = dynamic_news_helper.PickMap(level.name()) for i=1,65534 do local se_obj = sim:object(i) if se_obj and (lvl_name == sim:level_name(gg:vertex(se_obj.m_game_vertex_id):level_id())) then local cls = se_obj:clsid() local sec = se_obj:section_name() if IsStalker(nil,cls) and sfind(sec,"sim_default_") then local comm = se_obj:community() if (comm ~= "trader") and (comm ~= "zombied") then stalkers[#stalkers + 1] = i end elseif IsMonster(nil,cls) and monster_tiers[cls] and (monster_tiers[cls] > 0) then monsters[#monsters + 1] = cls end end end if (#stalkers == 0) then return end local sender_id = stalkers[math.random(#stalkers)] local sender_se = alife_object(sender_id) if (not sender_se) then return false end local sender_comm = sender_se:community() local fac_profile = dynamic_news_helper.GetFaction(sender_comm) if (not fac_profile) then return false end if not (self.channel_status[sender_comm]) then return end local sender_name = sender_se:character_name() local sender_desc = game.translate_string("st_dyn_news_comm_" .. sender_comm .. "_" .. tostring(6)) local map = game.translate_string(lvl_name) local location = dynamic_news_helper.GetPointDescription(sender_se) local sender_type = fac_profile["type"] if (ini_sys:r_string_ex("string_table","language") == "rus") then map = game.translate_string("st_dyn_news_" .. lvl_name) end for i=1,#stalkers do local se_obj = sim:object(stalkers[i]) if se_obj then local comm = se_obj:community() if game_relations.is_factions_enemies(sender_comm, comm) then enemies[#enemies + 1] = comm elseif (sender_comm ~= comm) then naturals[#naturals + 1] = comm end end end local types = {"none"} local target_desc = {} target_desc["none"] = "" if (#enemies > 0) then local enemy_comm = enemies[math.random(#enemies)] target_desc["enemy"] = game.translate_string("st_dyn_news_comm_" .. enemy_comm .. "_8") if (not self.unknown[enemy_comm]) then types[#types + 1] = "enemy" end end if (#naturals > 0) then local natural_comm = naturals[math.random(#naturals)] target_desc["friend"] = game.translate_string("st_dyn_news_comm_" .. natural_comm .. "_8") types[#types + 1] = "friend" end if (#monsters > 0) then local mutant_cls = monsters[math.random(#monsters)] target_desc["mutant"] = dynamic_news_helper.GetMonsterDescription(nil,7,nil,mutant_cls) if (ini_sys:r_string_ex("string_table","language") == "rus") then target_desc["mutant"] = dynamic_news_helper.GetMonsterDescription(nil,6,nil,mutant_cls) end types[#types + 1] = "mutant" end local target_type = types[math.random(#types)] local target = target_desc[target_type] -- preparing the news local tbl = utils_data.collect_translations("st_dyn_news_zone_activity_" .. sender_type .. "_" .. target_type .. "_",true) if (not tbl) then return false end local msg_pick = tbl[math.random(#tbl)] -- if news involving special character, see if they are alive local special = { ["leader"] = "", ["trader"] = "", ["mechanic"] = "", --["medic"] = "", --["barman"] = "", --["guide"] = "", } for k,v in pairs(special) do if string.find(msg_pick,"$"..k) then local npc_info = get_story_npc_info(fac_profile[k]) if npc_info then special[k] = npc_info.name else printf("~ News couldn't be sent because it involves special character [%s], he's missing? \nMessage: %s", fac_profile[k], msg_pick) return false end end end local Se = game.translate_string("st_faction_" .. sender_comm) local msg = utils_data.parse_string_keys( msg_pick , { ["speaker"]=sender_name , ["target"]=target , ["map"]=map , ["location"]=location , ["leader"]=special["leader"] , ["trader"]=special["trader"] , ["mechanic"]=special["mechanic"] } ) --dynamic_news_helper.send_tip(msg,Se, nil, msg_duration,sender_comm,"beep_1","gr") self:PushToChannel(sender_comm, {Mg=msg,Ic=sender_comm,Snd="beep_1",Se=Se,It="gr"}) end function DynamicNewsManager:SpamRandom() -- random news from all over the zone local clr_1 = "%" .. "%c[255,160,160,190]" local clr_2 = "%" .. "%c[255,220,220,220]" local tbl = utils_data.collect_translations("st_dyn_news_spam_", true) if not tbl then return false end local Se = game.translate_string("st_dyn_news_sender_com_centre") local msg = utils_data.parse_string_keys(tbl[math.random(#tbl)], {["clr_1"]=clr_1, ["clr_2"]=clr_2}) -- Push spam news to the general channel self:PushToChannel("general", {Mg=msg, Ic="common", Snd="beep_2", Se=Se, It="gr"}) return true end function DynamicNewsManager:FoundDead() -- report dead body findings --printf(">>> Dyn News: FoundDead - call") if (not self.sentences_fnames) or (not self.sentences_fnames) then return false end local clr_1 = "%" .. "%c[255,160,160,190]" local clr_2 = "%" .. "%c[255,220,220,220]" local a1 = self.sentences_fnames[math.random(#self.sentences_fnames)] local b1 = self.sentences_snames[math.random(#self.sentences_snames)] local a2 = self.sentences_fnames[math.random(#self.sentences_fnames)] local b2 = self.sentences_snames[math.random(#self.sentences_snames)] local c = dynamic_news_helper.PickMap(level.name()) c = game.translate_string(c) local tbl = utils_data.collect_translations("st_dyn_news_death_reason_",true) if (not tbl) then return false end local y1 = string.char(math.random(65,90)) local y2 = string.char(math.random(65,90)) local z1 = tostring(math.random(9)) local z2 = tostring(math.random(9)) local z3 = tostring(math.random(9)) local Se = strformat("%s %s",a1,b1) local msg = utils_data.parse_string_keys( "$clr_1$death_report -$clr_2 $fname $sname, $map, $death_reason, $y1$y2-$z1$z2$z3" , {["clr_1"]=clr_1 , ["clr_2"]=clr_2 , ["death_report"]=game.translate_string("st_dyn_news_spc_death_report") , ["fname"]=a2 , ["sname"]=b2 , ["map"]=c , ["death_reason"]=tbl[math.random(#tbl)] , ["y1"]=y1 , ["y2"]=y2 , ["z1"]=z1 , ["z2"]=z2 , ["z3"]=z3}) --dynamic_news_helper.send_tip(msg,Se, nil, msg_duration,"common","no_sound","gr") self:PushToChannel("general",{Mg=msg,Ic="common",Snd="no_sound",Se=Se,It="gr"}) return true end --< Special News >-- function DynamicNewsManager:GossipTime() -- talk about time of day --printf(">>> Dyn News: GossipTime - call") if (not shw_time) then return false end local sender = self:FindSpeakerRandom() if self:IsSpecialNPC(sender) or self:IsMonoCommunity(sender) then return false end local TimeStringPharse = dynamic_news_helper.GetTimePharseAsString() local tbl = utils_data.collect_translations("st_dyn_news_time_" .. TimeStringPharse .. "_",true) if (not tbl) then return false end local Se = strformat("%s, %s",sender:character_name(),dynamic_news_helper.GetCommunityDescription(sender,6)) self:PushToChannel(sender:character_community(),{Mg=tbl[math.random(#tbl)],Ic=sender:character_icon(),Snd="beep_1",Se=Se,It="npc"}) return true end function DynamicNewsManager:GossipWeather() -- talk about the weather --printf(">>> Dyn News: GossipWeather - call") -- no weather news in night local hrs = level.get_time_hours() if (hrs >= 21) or (hrs < 5) then return false end local sender = self:FindSpeakerRandom() if self:IsSpecialNPC(sender) or self:IsMonoCommunity(sender) then return false end --type of weather: {"clear","partly","cloudy","foggy","rain","storm"} local WeatherNews local _WM = level_weathers.get_weather_manager() local WeatherType = _WM:get_curr_weather() if not (WeatherType) then return false end -- weather accurate check if (WeatherType == "rain") or (WeatherType == "storm") then local RainFactor = level.rain_factor() if (RainFactor >= 0.5) then WeatherNews = "storm" elseif (RainFactor > 0) and (RainFactor < 0.5) then WeatherNews = "rain" else -- if rainy weather but there's no actual rain return false end elseif (WeatherType == "foggy") then local FarDistance = weather.get_value_numric("fog_distance") if FarDistance and (FarDistance < 200) then WeatherNews = "foggy" else -- if foggy weather but there's no actual fog return false end elseif (level.rain_factor() <= 0) then WeatherNews = WeatherType else return false end local tbl = utils_data.collect_translations("st_dyn_news_weather_" .. WeatherNews .. "_",true) if (not tbl) then return false end local Se = strformat("%s, %s",sender:character_name(),dynamic_news_helper.GetCommunityDescription(sender,6)) self:PushToChannel(sender:character_community(),{Mg=tbl[math.random(#tbl)],Ic=sender:character_icon(),Snd="beep_1",Se=Se,It="npc"}) return true end function DynamicNewsManager:GossipNearbyActivity() -- talk about movement of nearby enemy --printf(">>> Dyn News: GossipNearbyActivity - call") local sender,target = self:FindSpeakerAndTarget(true,250) if (not sender) or (not target) then return false end if self:IsSpecialNPC(sender) or self:IsMonoCommunity(sender) then return false end local who local activity = "enemy_activity_" if IsStalker(target,target:clsid()) then local target_comm = target:character_community() if self.unknown[target_comm] then return false end if target_comm == "zombied" then activity = activity .. "zombied" else activity = activity .. "stalker" end if (ini_sys:r_string_ex("string_table","language") == "rus") then who = dynamic_news_helper.GetCommunityDescription(target,math.random(3,4)) else who = dynamic_news_helper.GetCommunityDescription(target,math.random(1,4)) end elseif IsMonster(target,target:clsid()) then local cls = dynamic_news_helper.GetMonsterDescription(target) local tier = dynamic_news_helper.GetMutant(cls,"tier") activity = activity .. "mutant_" .. tostring(tier) who = dynamic_news_helper.GetMonsterDescription(target,1) end local tbl = utils_data.collect_translations("st_dyn_news_" .. activity .. "_",true) if (not tbl) or (not who) then return false end local msg = utils_data.parse_string_keys( tbl[math.random(#tbl)] , {["who"]=who} ) local Se = strformat("%s, %s",sender:character_name(),dynamic_news_helper.GetCommunityDescription(sender,6)) self:PushToChannel(sender:character_community(),{Mg=msg,Ic=sender:character_icon(),Snd="beep_2",Se=Se,It="npc",Ty=activity,Id=sender}) return true end function DynamicNewsManager:DumbZombie() -- zombies trying to talk over the radio --printf(">>> Dyn News: DumbZombie - call") local sender = self:FindSpeakerRandom(true,"zombied") -- must use channel_status["general"] for intended zombied case if self:IsSpecialNPC(sender) or self:IsMonoCommunity(sender) then return false end local tbl = utils_data.collect_translations("st_dyn_news_dumb_zombie_",true) if (not tbl) then return false end local Se = strformat("%s, %s",sender:character_name(),dynamic_news_helper.GetCommunityDescription(sender,6)) self:PushToChannel(sender:character_community(),{Mg=tbl[math.random(#tbl)],Ic=sender:character_icon(),Snd="beep_2",Se=Se,It="npc",Ty="dumb_zombie"}) return true end function DynamicNewsManager:monster_on_respawn(npc,near) -- report mutant activity [empty] end --< Task News >-- function DynamicNewsManager:GossipBounty() -- talk about the player's bounty --printf(">>> Dyn News: GossipBounty - call") local bounty for task_id,npc_id in pairs(axr_task_manager.bounties_by_id) do -- loop across npcs on task list bounty = db.storage[npc_id] and db.storage[npc_id].object if (bounty and bounty:alive()) then break end end if not (bounty and bounty:alive()) then -- if the bounty npc is dead, return return false end local sender = self:FindSpeakerNoVictim(bounty,false,true) if self:IsSpecialNPC(sender) or self:IsMonoCommunity(sender) then return false end local tbl = utils_data.collect_translations("st_dyn_news_gossip_bounty_",true) if (not tbl) then return false end local Se = strformat("%s, %s",sender:character_name(),dynamic_news_helper.GetCommunityDescription(sender,6)) local msg = utils_data.parse_string_keys( tbl[math.random(#tbl)] , { ["who"]=bounty:character_name() , ["where"]=dynamic_news_helper.GetPointDescription(bounty) } ) self:PushToChannel(sender:character_community(),{Mg=msg,Ic=sender:character_icon(),Snd="news",Se=Se,It="npc"}) return true end function DynamicNewsManager:GossipAlphaSquad() -- talk about the player being a bounty --printf(">>> Dyn News: GossipAlphaSquad - call") local se_actor = alife():actor() actor_name = se_actor:character_name() local sq = sim_squad_bounty.get_active_squads() if (not sq) then return false end local alpha_npcs for k,v in pairs(sq) do alpha_npcs = alpha_npcs or {} local se_sq = alife_object(k) if se_sq and (se_sq:section_name() == v) and (simulation_objects.is_on_the_same_level(se_actor, se_sq)) then for j in se_sq:squad_members() do local npc = db.storage[j.id] and db.storage[j.id].object if npc and npc:alive() then alpha_npcs[npc:id()] = true end end end end if (not alpha_npcs) then -- if no alpha squad nearby, return return false end local sender = self:FindSpeakerNoVictim(db.actor,false,true) if self:IsSpecialNPC(sender) or self:IsMonoCommunity(sender) then return false end if alpha_npcs[sender:id()] then return end local tbl = utils_data.collect_translations("st_dyn_news_gossip_bounty_",true) if (not tbl) then return false end local Se = strformat("%s, %s",sender:character_name(),dynamic_news_helper.GetCommunityDescription(sender,6)) local msg = utils_data.parse_string_keys( tbl[math.random(#tbl)] , { ["who"]=actor_name , ["where"]=dynamic_news_helper.GetPointDescription(db.actor) } ) self:PushToChannel(sender:character_community(),{Mg=msg,Ic=sender:character_icon(),Snd="news",Se=Se,It="npc"}) return true end function DynamicNewsManager:GossipTaskRepeatTimeout(task_id) --printf(">>> Dyn News: GossipTaskRepeatTimeout - call") local story_id = task_id:sub(1,-8) local obj = get_story_object(story_id) if not (obj) then return false end local sender = self:FindSpeakerNoVictim(nil,nil,true) if (not sender) then return false end local tbl = utils_data.collect_translations("st_dyn_news_gossip_hostage_",true) if (not tbl) then return false end local Se = strformat("%s, %s",sender:character_name(),dynamic_news_helper.GetCommunityDescription(sender,6)) local msg = strformat(tbl[math.random(#tbl)],obj:character_name()) self:PushToChannel(sender:character_community(),{Mg=msg,Ic=sender:character_icon(),Snd="news",Se=Se,It="npc"}) return true end function DynamicNewsManager:GossipTaskLL() local actor_comm = get_actor_true_community() local sender = self:FindSpeakerRandom(false,actor_comm) if (not sender) then return false end local comm = sender:character_community() if (not self.channel_status[comm]) or self.mono[comm] or self.unknown[comm] then return end local tbl = utils_data.collect_translations("st_dyn_news_gossip_task_LL_",true) if (not tbl) then return false end local Se = strformat("%s, %s",sender:character_name(),dynamic_news_helper.GetCommunityDescription(sender,6)) local msg = tbl[math.random(#tbl)] self:PushToChannel(comm,{Mg=msg,Ic=sender:character_icon(),Snd="news",Se=Se,It="npc"}) story_LL_trigger = false return true end function DynamicNewsManager:GossipTaskMS() local actor_comm = get_actor_true_community() local sender = self:FindSpeakerRandom(false,actor_comm) if (not sender) then return false end local comm = sender:character_community() if (not self.channel_status[comm]) or self.mono[comm] or self.unknown[comm] then return end local tbl = utils_data.collect_translations("st_dyn_news_gossip_task_MS_",true) if (not tbl) then return false end local Se = strformat("%s, %s",sender:character_name(),dynamic_news_helper.GetCommunityDescription(sender,6)) local msg = tbl[math.random(#tbl)] self:PushToChannel(comm,{Mg=msg,Ic=sender:character_icon(),Snd="news",Se=Se,It="npc"}) story_MS_trigger = false return true end function DynamicNewsManager:GossipTaskOA() local actor_comm = get_actor_true_community() local f = faction_expansions.faction[actor_comm] local leader = f and f["leader"] local npc_info = leader and get_story_npc_info(leader) local Se = npc_info and npc_info.name local Ico = npc_info and npc_info.icon if not (Se and Ico) then return end local msg = game.translate_string("st_dyn_news_gossip_task_oa_" .. actor_comm) dynamic_news_helper.send_tip(msg, Se, math.random(20,30), 20, Ico, "news", "npc") end function DynamicNewsManager:GossipTaskDP() local tbl = utils_data.collect_translations("st_dyn_news_gossip_task_dp_",true) if (not tbl) then return false end local msg = tbl[math.random(#tbl)] local player_name = alife():actor():character_name() msg = strformat(msg, player_name, Se) dynamic_news_helper.send_tip(msg, Se, math.random(20,30), 20, "ui_inGame2_D_gonets_pravosudiya", "news", "npc") end function DynamicNewsManager:GossipTaskDRX() local actor_comm = get_actor_true_community() local f = faction_expansions.faction[actor_comm] local leader = f and f["leader"] local npc_info = leader and get_story_npc_info(leader) local Se = npc_info and npc_info.name local Ico = npc_info and npc_info.icon if not (Se and Ico) then return end local msg = game.translate_string("drx_sl_start_msg_" .. actor_comm) local actor_name = alife():actor():character_name() msg = strformat(msg,actor_name) dynamic_news_helper.send_tip(msg,Se, 0, 20,Ico,"news","npc") end --< Companions News >-- function DynamicNewsManager:CompanionAboutLife(sender) -- companion chitchat --printf(">>> Dyn News: CompanionAboutLife - call") if self:IsSpecialNPC(sender) or self:IsMonoCommunity(sender) then return false end local alife = alife() local se_actor = alife:actor() local tbl if (math.random(2) == 1) then tbl = utils_data.collect_translations("st_dyn_news_companion_life_all_",true) else tbl = utils_data.collect_translations("st_dyn_news_companion_life_" .. sender:character_community() .. "_",true) end if (not tbl) then return false end local msg = tbl[math.random(#tbl)] if string.match(msg,"$name") then msg = utils_data.parse_string_keys( msg , { ["name"]=se_actor:character_name() } ) end local Se = strformat("%s, %s",sender:character_name(),game.translate_string("st_dyn_news_sender_companion")) self:PushToChannel(sender:character_community(),{Mg=msg,Ic=sender:character_icon(),Snd="beep_2",Se=Se,It="npc"}) return true end function DynamicNewsManager:CompanionAboutLevel(sender) -- companion talk about the current map --printf(">>> Dyn News: CompanionAboutLevel call") if self:IsSpecialNPC(sender) or self:IsMonoCommunity(sender) then return false end local lvl = level.name() local tbl if (dynamic_news_helper.GetFaction(sender:character_community(),"territory") == level.name()) then tbl = utils_data.collect_translations("st_dyn_news_companion_level_home_",true) else tbl = utils_data.collect_translations("st_dyn_news_companion_level_" .. lvl .. "_",true) end if (not tbl) then return false end local Se = strformat("%s, %s",sender:character_name(),game.translate_string("st_dyn_news_sender_companion")) self:PushToChannel(sender:character_community(),{Mg=tbl[math.random(#tbl)],Ic=sender:character_icon(),Snd="beep_1",Se=Se,It="npc"}) return true end function DynamicNewsManager:CompanionAboutActor(sender) -- companion talk about player's rank and reputation if self:IsSpecialNPC(sender) or self:IsMonoCommunity(sender) then return false end local alife = alife() local se_actor = alife:actor() local a,b local rep_tbl = {"terrible","really_bad","very_bad","bad","neutral","good","very_good","really_good","excellent"} local rank_tbl = {"novice","trainee","experienced","professional","veteran","expert","master","legend"} local actor_rep = utils_obj.get_reputation_name(db.actor:character_reputation()) local actor_rank = ranks.get_obj_rank_name(db.actor) for i=1,#rep_tbl do if string.match(actor_rep,rep_tbl[i]) then if (i <= 4) then a = 1 elseif (i > 4) and (i < 7) then a = 2 elseif (i >= 7) then a = 3 end break end end for i=1,#rank_tbl do if string.match(actor_rank,rank_tbl[i]) then if (i < 3) then b = 1 elseif (i >= 3) and (i < 5) then b = 2 elseif (i >= 5) and (i < 7) then b = 3 elseif (i == 7) then b = 4 end break end end local tbl if (math.random(2) == 1) then tbl = utils_data.collect_translations("st_dyn_news_companion_rep_" .. tostring(a) .. "_",true) else tbl = utils_data.collect_translations("st_dyn_news_companion_rank_" .. tostring(b) .. "_",true) end if (not tbl) then return false end local Se = strformat("%s, %s",sender:character_name(),game.translate_string("st_dyn_news_sender_companion")) local msg = utils_data.parse_string_keys( tbl[math.random(#tbl)] , { ["name"]=se_actor:character_name() } ) --printf(">>> Dyn News: CompanionAboutActor | Rank: " .. actor_rank .. ", Reputation: " .. actor_rep) self:PushToChannel("general",{Mg=msg,Ic=sender:character_icon(),Snd="beep_1",Se=Se,It="npc"}) return true end --< Reaction News >-- function DynamicNewsManager:GossipEmissionEnd(what) -- stalkers reacting about the surge --printf(">>> Dyn News: GossipEmissionEnd - call") if (not shw_reaction) or ((what ~= "emission") and (what ~= "storm")) then return false end local sender = self:FindSpeakerRandom() if self:IsSpecialNPC(sender) or self:IsMonoCommunity(sender) then return false end local tbl = utils_data.collect_translations("st_dyn_news_surge_after_",true) if (not tbl) then return false end local Se = strformat("%s, %s",sender:character_name(),dynamic_news_helper.GetCommunityDescription(sender,6)) local msg = utils_data.parse_string_keys( tbl[math.random(#tbl)] , { ["what"]=what } ) dynamic_news_helper.send_tip(msg,Se,math.random(10,15),msg_duration,sender:character_icon(),"beep_1","npc") -- no need for push to channel self.surge_type = "" -- reset return true end function DynamicNewsManager:ResponseOnFoundArtefact(who) -- react to people who found artifacts recently --printf(">>> Dyn News: ResponseOnFoundArtefact - call") local sender = self:FindSpeakerNoVictim(who,true,true) if self:IsSpecialNPC(sender) or self:IsMonoCommunity(sender) then return false end local tbl = utils_data.collect_translations("st_dyn_news_res_artefact_",true) if (not tbl) then return false end local Se = strformat("%s, %s",sender:character_name(),dynamic_news_helper.GetCommunityDescription(sender,6)) self.response["message"] = {Mg=tbl[math.random(#tbl)],Ic=sender:character_icon(),Snd="beep_1",Se=Se,It="npc",To=false,St=msg_duration} return true end function DynamicNewsManager:ResponseOnFoundStash() -- react to people who lost their stashes --printf(">>> Dyn News: ResponseOnFoundStash - call") if (not self.sentences_fnames) or (not self.sentences_fnames) then return false end local tbl = utils_data.collect_translations("st_dyn_news_res_found_stash_",true) if (not tbl) then return false end local Se = strformat("%s %s" , self.sentences_fnames[math.random(#self.sentences_fnames)] , self.sentences_snames[math.random(#self.sentences_snames)]) self.response["message"] = {Mg=tbl[math.random(#tbl)],Ic="common",Snd="no_sound",Se=Se,It="gr",To=false,St=msg_duration} return true end function DynamicNewsManager:ResponseOnBoughtItems(who) -- react to people who bought items recently --printf(">>> Dyn News: ResponseOnBoughtItems - call") local sender = self:FindSpeakerNoVictim(who,true,true) if (not sender) then return false end local tbl = utils_data.collect_translations("st_dyn_news_res_trade_",true) if (not tbl) then return false end local Se = strformat("%s, %s",sender:character_name(),dynamic_news_helper.GetCommunityDescription(sender,6)) self.response["message"] = {Mg=tbl[math.random(#tbl)],Ic=sender:character_icon(),Snd="beep_1",Se=Se,It="npc",To=false,St=msg_duration} return true end function DynamicNewsManager:ResponseOnGossipNearbyActivity(who,activity) -- react to people reporting nearby activity --printf(">>> Dyn News: ResponseOnGossipNearbyActivity - call") local sender = self:FindSpeakerNoVictim(who,true,true) if (not sender) then return false end local tbl = utils_data.collect_translations("st_dyn_news_res_" .. activity .. "_",true) if (not tbl) then return false end local Se = strformat("%s, %s",sender:character_name(),dynamic_news_helper.GetCommunityDescription(sender,6)) self.response["message"] = {Mg=tbl[math.random(#tbl)],Ic=sender:character_icon(),Snd="beep_1",Se=Se,It="npc",To=false,St=msg_duration} return true end function DynamicNewsManager:ResponseOnDumbZombie() -- react to dumb zombies being dumb --printf(">>> Dyn News: ResponseOnDumbZombie - call") local sender = self:FindSpeakerRandom(true) if self:IsSpecialNPC(sender) or self:IsMonoCommunity(sender) then return false end local tbl = utils_data.collect_translations("st_dyn_news_res_dumb_zombie_",true) if (not tbl) then return false end local Se = strformat("%s, %s",sender:character_name(),dynamic_news_helper.GetCommunityDescription(sender,6)) self.response["message"] = {Mg=tbl[math.random(#tbl)],Ic=sender:character_icon(),Snd="beep_1",Se=Se,It="npc",To=false,St=msg_duration} return true end function DynamicNewsManager:ResponseOnDeathByStalker(who) -- react to stalker's death by another stalker --printf(">>> Dyn News: ResponseOnDeathByStalker - call") local sender = self:FindSpeakerNoVictim(who,true,true) if (not sender) then return false end local tbl = utils_data.collect_translations("st_dyn_news_res_death_stalker_",true) if (not tbl) then return false end local Se = strformat("%s, %s",sender:character_name(),dynamic_news_helper.GetCommunityDescription(sender,6)) self.response["message"] = {Mg=tbl[math.random(#tbl)],Ic=sender:character_icon(),Snd="beep_1",Se=Se,It="npc",To=false,St=msg_duration} return true end function DynamicNewsManager:ResponseOnDeathByMutant(who) -- react to stalker's death by mutants --printf(">>> Dyn News: ResponseOnDeathByMutant - call") local sender = self:FindSpeakerNoVictim(who,true,true) if (not sender) then return false end local tbl = utils_data.collect_translations("st_dyn_news_res_death_",true) if (not tbl) then return false end local Se = strformat("%s, %s",sender:character_name(),dynamic_news_helper.GetCommunityDescription(sender,6)) self.response["message"] = {Mg=tbl[math.random(#tbl)],Ic=sender:character_icon(),Snd="beep_1",Se=Se,It="npc",To=false,St=msg_duration} return true end function DynamicNewsManager:ResponseOnDeathBySurges(who) -- react to stalker's death by surges [NOT USED] --printf(">>> Dyn News: ResponseOnDeathBySurges - call") local sender = self:FindSpeakerNoVictim(who,true,true) if (not sender) then return false end local tbl = utils_data.collect_translations("st_dyn_news_res_death_surge_",true) if (not tbl) then return false end local Se = strformat("%s, %s",sender:character_name(),dynamic_news_helper.GetCommunityDescription(sender,6)) self.response["message"] = {Mg=tbl[math.random(#tbl)],Ic=sender:character_icon(),Snd="beep_1",Se=Se,It="npc",To=false,St=msg_duration} return true end function DynamicNewsManager:ResponseOnDeathByStalker_Fake() -- react to stalker's death by another stalker (fake) --printf(">>> Dyn News: ResponseOnDeathByStalker_Fake - call") if (not self.sentences_fnames) or (not self.sentences_fnames) then return false end local tbl = utils_data.collect_translations("st_dyn_news_res_fake_death_stalker_",true) if (not tbl) then return false end local Se = strformat("%s %s" , self.sentences_fnames[math.random(#self.sentences_fnames)] , self.sentences_snames[math.random(#self.sentences_snames)]) self.response["message"] = {Mg=tbl[math.random(#tbl)],Ic="common",Snd="no_sound",Se=Se,It="gr",To=false,St=msg_duration} return true end function DynamicNewsManager:ResponseOnDeathByMutant_Fake() -- react to stalker's death by mutants (fake) --printf(">>> Dyn News: ResponseOnDeathByMutant_Fake - call") if (not self.sentences_fnames) or (not self.sentences_fnames) then return false end local tbl = utils_data.collect_translations("st_dyn_news_res_fake_death_mutant_",true) if (not tbl) then return false end local Se = strformat("%s %s" , self.sentences_fnames[math.random(#self.sentences_fnames)] , self.sentences_snames[math.random(#self.sentences_snames)]) self.response["message"] = {Mg=tbl[math.random(#tbl)],Ic="common",Snd="no_sound",Se=Se,It="gr",To=false,St=msg_duration} return true end function DynamicNewsManager:ResponseOnDeathBySurges_Fake() -- react to stalker's death by surges (fake) --printf(">>> Dyn News: ResponseOnDeathBySurges_Fake - call") if (not self.sentences_fnames) or (not self.sentences_fnames) then return false end local tbl = utils_data.collect_translations("st_dyn_news_res_fake_death_surge_",true) if (not tbl) then return false end local Se = strformat("%s %s" , self.sentences_fnames[math.random(#self.sentences_fnames)] , self.sentences_snames[math.random(#self.sentences_snames)]) self.response["message"] = {Mg=tbl[math.random(#tbl)],Ic="common",Snd="no_sound",Se=Se,It="gr",To=30,St=msg_duration} return true end ------------------------------------------------------------ -- U.D.E - Pandablyat 18/05/2024 ------------------------------------------------------------ ------------------------------------------------------------ -- Helpers ------------------------------------------------------------ function DynamicNewsManager:BuildSentenceStalkerEnemy(victim,who,mn,mx) local comm_victim = victim:character_community() local comm_who = who:character_community() local comm, dist local c = 0 for i=1, #db.OnlineStalkers do if (db.OnlineStalkers[i] ~= who_id) then local st = db.storage[db.OnlineStalkers[i]] local npc = st and st.object or level.object_by_id(db.OnlineStalkers[i]) if (npc and IsStalker(npc,npc:clsid()) and npc:alive() and not get_object_story_id(db.OnlineStalkers[i])) then comm = npc:character_community() -- remove local if (comm == comm_who) then dist = victim:position():distance_to(npc:position()) if (dist < mx) or (dist > mn) then c = c + 1 end end end end if (c >= 2) then break end end local sentences = {} local string_count = 1 while true do local tr_s = game.translate_string("st_dyn_news_builder_attacked_"..string_count) if (tr_s == "st_dyn_news_builder_attacked_"..string_count) then break else table.insert(sentences,tr_s) end string_count = string_count + 1 end if (#sentences == 0) then return false end if (c >= 2) then return sentences[math.random(#sentences)], dynamic_news_helper.GetCommunityDescription(who,math.random(9,10)) end return sentences[math.random(#sentences)], dynamic_news_helper.GetCommunityDescription(who,math.random(7,8)) end function DynamicNewsManager:BuildSentenceStalkerEnemy_Offline(comm_w) local index = math.random(7,8) if (math.random(100) < 50) then index = math.random(9,10) end local sentences = utils_data.collect_translations("st_dyn_news_builder_attacked_",true) if (not sentences) then return false end local comm_w_desc = game.translate_string("st_dyn_news_comm_" .. comm_w .. "_" .. index) return sentences[math.random(#sentences)], comm_w_desc end function DynamicNewsManager:IsSpecialNPC(npc) if (not npc) then return true end if string.find(npc:section(),"sim_default") then return false end return true end function DynamicNewsManager:IsMonoCommunity(npc) if (not npc) then return true end local comm = character_community(npc) if (npc:id() == AC_ID and comm ~= "actor") then comm = comm:sub(7) end if self.mono[comm] then return true end return false end function DynamicNewsManager:IsUnknownCommunity(npc) if (not npc) then return true end local comm = character_community(npc) if (npc:id() == AC_ID and comm ~= "actor") then comm = comm:sub(7) end if self.unknown[comm] then return true end return false end function get_story_npc_info(section) if not (section and section ~= "") then return end local se_npc = get_story_se_object(section) if se_npc then local name = se_npc:character_name() local icon = se_npc:character_icon() return { name = name , icon = icon } end return end