249 lines
7 KiB
Text
249 lines
7 KiB
Text
--[[------------------------------------------------------------------------------------------------------------------
|
||
Управление диалогами групп сталкеров
|
||
Чугай Саша
|
||
|
||
Сделать:
|
||
|
||
Описание:
|
||
Чтоб в какой-то момент начался диалог, нужно, чтоб у ВСЕХ членов команды был вызван enable. Один единственный вызов
|
||
disable и диалог не начнётся. Для начала диалога выбирается любой член команды, если нету никого рядом с ним, то диалог
|
||
не начнётся. Для повторных реплик выбирается любой член команды (кроме только что говорившего) из числа тех, кто нахо-
|
||
дится рядом с последним говорившим; если таких нету, то диалог прерывается.
|
||
--------------------------------------------------------------------------------------------------------------------]]
|
||
|
||
local teams = {} -- группы сталкеров, сгруппированных для диалога
|
||
local snd = { themes = {} }
|
||
|
||
local idle_min, idle_max = 20, 60 -- пределы периода молчанки между диалогами в секундах
|
||
local pause_min, pause_max = 1, 2 -- пределы величины паузы между фразаим в секундах
|
||
local dist_max = 100 -- квадрат максимального расстояния между собеседниками
|
||
|
||
local avail = {}
|
||
|
||
----------------------------------------------------------------------------------------------------------------------
|
||
-- деревья диалогов
|
||
----------------------------------------------------------------------------------------------------------------------
|
||
local dialogs = {
|
||
place = {
|
||
[1] = { snd = "place1", next = 2 },
|
||
[2] = { snd = "place2" }
|
||
},
|
||
}
|
||
|
||
----------------------------------------------------------------------------------------------------------------------
|
||
-- класс управления диалогами (только звуками)
|
||
----------------------------------------------------------------------------------------------------------------------
|
||
class "dialog_mgr"
|
||
|
||
function dialog_mgr:__init()
|
||
end
|
||
|
||
function dialog_mgr:reset( npc, team )
|
||
self.object = npc
|
||
|
||
if self.team ~= team then
|
||
-- если сталкер в команде
|
||
if self.team then
|
||
-- убрать из текущей команды
|
||
self:finalize()
|
||
end
|
||
|
||
self.team = team
|
||
|
||
if team then
|
||
if not teams[team] then
|
||
teams[team] = { npcs = {}, npcs_num = 0, idle_end = 0 }
|
||
end
|
||
|
||
self.a = teams[team]
|
||
-- self.a.npcs[npc:id()] = npc
|
||
table.insert( self.a.npcs, npc )
|
||
|
||
-- printf( "[dialogs] adding %s to dlgteam %s", npc:name(), team )
|
||
end
|
||
end
|
||
end
|
||
|
||
function dialog_mgr:finalize()
|
||
if self.team then
|
||
-- убрать сталкера из списка
|
||
for i, v in pairs(self.a.npcs) do
|
||
if v:id() == self.object:id() then
|
||
table.remove( self.a.npcs, i )
|
||
break
|
||
end
|
||
end
|
||
|
||
self.team = nil
|
||
|
||
-- если никого не осталось, сбросить диалог
|
||
if #self.a.npcs == 0 then
|
||
self:reset_dialog()
|
||
end
|
||
end
|
||
end
|
||
|
||
-- включить возможность начала диалога
|
||
function dialog_mgr:enable( dlgs )
|
||
if self.team then
|
||
self.a.dlgs = dlgs
|
||
end
|
||
end
|
||
|
||
-- выключить возможность начала диалога
|
||
function dialog_mgr:disable()
|
||
if self.team then
|
||
self.a.dlgs = nil
|
||
end
|
||
end
|
||
|
||
-- текущий активный диалог
|
||
function dialog_mgr:active()
|
||
return self.team and self.a.cur_dlg
|
||
end
|
||
|
||
-- начало нового диалога
|
||
function dialog_mgr:new_dialog()
|
||
-- выбор нового диалога
|
||
if #self.a.dlgs > 1 then
|
||
local r = math.random( 1, #self.a.dlgs - 1 )
|
||
if self.a.dlgs[r] == self.a.last_dlg then
|
||
r = r + 1
|
||
end
|
||
|
||
self.a.cur_dlg = self.a.dlgs[r]
|
||
else
|
||
self.a.cur_dlg = self.a.dlgs[1]
|
||
end
|
||
|
||
self.a.last_dlg = self.a.cur_dlg
|
||
|
||
-- printf( "!!!!! NEW_DIALOG="..self.a.cur_dlg )
|
||
-- попробовать выбрать первую фразу
|
||
if not self:new_phrase() then
|
||
-- если не смогли, то диалог начаться не может
|
||
self:reset_dialog()
|
||
-- следующую проверку возможности начала диалога отложить на небольшое время
|
||
self.a.idle_end = time_global() + 5000
|
||
end
|
||
end
|
||
|
||
-- сбросить диалог
|
||
function dialog_mgr:reset_dialog()
|
||
self.a.dlgs = nil
|
||
self.a.cur_dlg = nil
|
||
self.a.cur_phrase = nil
|
||
self.a.cur_npc = nil
|
||
|
||
-- следующий диалог может начаться лишь по истечению периода молчанки
|
||
self.a.idle_end = time_global() + math.random( idle_min, idle_max ) * 1000
|
||
|
||
-- printf( "reset dialog" )
|
||
end
|
||
|
||
-- отменить текущий период молчанки между диалогами
|
||
function dialog_mgr:reset_idle()
|
||
if self.team then
|
||
self.a.idle_end = 0
|
||
end
|
||
end
|
||
|
||
-- подготовка новой фразы
|
||
function dialog_mgr:new_phrase()
|
||
clear_table( avail )
|
||
|
||
-- если диалог уже был начат (т.е. есть текущий говорящий)
|
||
if self.a.cur_npc then
|
||
-- выбрать того, кто ответит. он должен быть недалеко от предыдущего говорящего
|
||
for i, v in pairs(self.a.npcs) do
|
||
if v:id() ~= self.a.cur_npc:id() and self.a.cur_npc:position():distance_to_sqr( v:position() ) <= dist_max then
|
||
table.insert( avail, v )
|
||
end
|
||
end
|
||
|
||
if #avail == 0 then
|
||
-- если отвечать некому, то фраза не выбрана
|
||
return false
|
||
end
|
||
|
||
-- выбор нового говорящего
|
||
self.a.cur_npc = avail[math.random( 1, #avail )]
|
||
else
|
||
-- попытка выбора нового говорящего
|
||
self.a.cur_npc = self.a.npcs[math.random( 1, #self.a.npcs )]
|
||
|
||
-- посмотреть, может ли кто-то потенциально продолжить диалог
|
||
local flg
|
||
|
||
for i, v in pairs(self.a.npcs) do
|
||
if v:id() ~= self.a.cur_npc:id() and self.a.cur_npc:position():distance_to_sqr( v:position() ) <= dist_max then
|
||
flg = true
|
||
break
|
||
end
|
||
end
|
||
|
||
if not flg then
|
||
-- если нет, то даже и не начинать диалог
|
||
return false
|
||
end
|
||
end
|
||
|
||
-- выбор новой фразы
|
||
if not self.a.cur_phrase then
|
||
printf( self.a.cur_dlg )
|
||
self.a.cur_phrase = dialogs[self.a.cur_dlg][1]
|
||
else
|
||
local p = self.a.cur_phrase.next
|
||
if not p then
|
||
-- следующей фразы нету
|
||
return false
|
||
end
|
||
|
||
if type( p ) == "table" then
|
||
p = p[math.random( 1, #p )]
|
||
end
|
||
|
||
self.a.cur_phrase = dialogs[self.a.cur_dlg][p]
|
||
end
|
||
|
||
-- установить задержку перед новой фразой
|
||
self.a.phrase_delay_end = time_global() + math.random( pause_min, pause_max ) * 1000
|
||
self.a.phrase_playing = false
|
||
|
||
-- printf( "phrase selected" )
|
||
return true
|
||
end
|
||
|
||
-- обновление
|
||
function dialog_mgr:update()
|
||
-- если сталкер не состоит в команде для диалогов
|
||
if not self.team then
|
||
return
|
||
end
|
||
|
||
if self.a.cur_dlg then -- активен ли диалог?
|
||
if self.object:id() == self.a.cur_npc:id() then -- обновление текущего говорящего?
|
||
if self.a.phrase_delay_end < time_global() then -- окончилась ли необходимая задержка перед фразой?
|
||
if self.a.phrase_playing then -- начали ли уже играть звук?
|
||
if self.a.cur_npc:active_sound_count() == 0 then -- звук закончился?
|
||
if not self:new_phrase() then
|
||
-- если новая фраза не выбрана, то наступил конец диалога
|
||
self:reset_dialog()
|
||
end
|
||
end
|
||
else
|
||
-- произнести фразу
|
||
snd.themes[1] = self.a.cur_phrase.snd
|
||
xr_sound.sound_update( self.a.cur_npc, snd, true )
|
||
self.a.phrase_playing = true
|
||
-- printf( "SNDSNDNSNDND = " .. snd.themes[1] )
|
||
end
|
||
end
|
||
end
|
||
else
|
||
-- если можно, начать диалог и у нас не один говорящий
|
||
if self.a.dlgs and self.a.idle_end < time_global() and #self.a.npcs > 1 then
|
||
self:new_dialog()
|
||
end
|
||
end
|
||
end
|