BDS(Beo117 Dialog System) - beo117 - 08-02-14 08:05
~ Beo117 Dialog System ~
Opis:
Skrypt stworzyłem na potrzeby swojej gry, jest to napisany od podstaw system dialogów. Nie opiera się on tak jak oryginalny system z RMXP na wyborach, lecz na podziale każdego z dialogów na oddzielne wątki dla każdej z rozmów. BDS wzorowałem na systemie dialogów z Gothic i to powinno bardzo dużo wyjaśnić.
Na czym to właściwie polega? Prosty przykład jest taki, że mamy na początku dostępne trzy kwestie dialogowe. Po użyciu jednej dwie pozostałe nadal są dostępne i to bez żadnego kombinowania z przełącznikami itp.
Wspomnę od razu, że skrypt wymaga naprawdę sporych umiejętności w RGSS, jednak aby ułatwić start przygotowałem stronę poświęconą BDS.Nie jest jeszcze ukończona, pisze ją już dwa tygodnie, czyli sporo dłużej niż sam skrypt i już mnie to trochę zmęczyło, jednak będę ją uzupełniał :).
Zalety/Wady
Zalety:
- Umożliwia prowadzenie nieliniowej narracji
- Traktowanie każdej z rozmów jako oddzielny byt
- Każda postać w grze trzyma swój dialog w innym pliku, co poprawia organizację
Wady:
- Brak "fajerwerków", nie ma kolorowania tekstu, twarzy itp.
- Wymaga naprawdę dużych umiejętności RGSS, stworzyłem kurs jednak jak pamiętam ze swojego doświadczenia, nowicjusze będą mieli duże problemy
- Trochę dłubania przy tym jest :) Ale efekt na to zasługuje :)
Autor:
Skrypt napisałem sam od podstaw, jedynie jako inspirację biorąc system z gry Gothic.
Dodatkowe podziękowania idą dla Samo, The thief, ponieważ BDS wykorzystuje jego skrypt ATP(Advanced Text Paragrapher).
Strona skryptu:
http://bdshelp.cba.pl
Kanał YT:
https://www.youtube.com/channel/UCm_Xq8AQI7gRfiLlmpmg8_Q
(nie jest to kurs, a jedynie prezentacja efektów)
E-mail:
kursbds@gmail.com
Skrypt:
Ze skryptem jest taki problem, że składa się on z wielu elementów. Dlatego umieszczenie ich tutaj jest bardzo utrudnione. Szczególnie przez to, że BDS wprowadza zmiany w oryginalnych skryptach(w demie są one oznaczone gwiazdką, a edytowane linijki zostały odpowiednio skomentowane). Umieszczam cały kod, który stanowi ciało skryptu jednak pobranie dema i tak jest wymagane by zobaczyć edycję w oryginalnych skryptach.
Kod:
#===============================================================================
# * class DIA_Thread
# #klasa trzyma dane na temat wątku
#===============================================================================
class DIA_Thread
attr_accessor :name #tytuł_wątku
attr_accessor :repeat #czy_wątek_można_powtarzać
attr_accessor :instance #nazwa_głównej_funkcji
attr_accessor :active #nazwa_funkcji_sprawdzającej_czy_wątek_jest_aktywny
attr_accessor :onState #nazwa_funkcji_zawierającej_operacje_wątku
def initialize
@name = "tekst dialogu"
@repeat = false
@instance = "An instance of dialog"
@active = "When a thread is active"
@onState = "when a thread is on state"
end
end
#===============================================================================
# * module Dialog
# poprzez ten moduł wykonywane są wszystkie komendy dla systemu dialogowego
#===============================================================================
module Dialog
public
#-------------------------------------------------------------------------
# * initialize
#-------------------------------------------------------------------------
def self.start
$scene_dialog = Scene_DialogSystem.new
end
#-------------------------------------------------------------------------
# * update
#_------------------------------------------------------------------------
def self.update
$scene_dialog.update if $scene_dialog.dialogProces
end
#-------------------------------------------------------------------------
# * dispose
#_------------------------------------------------------------------------
def self.dispose
$scene_dialog.dispose
$scene_dialog = nil
end
#=========================================================================
#-------------------------------------------------------------------------
# * runDialog(dialog)
#_------------------------------------------------------------------------
def self.runDialog(dialog, force_dialog = nil)
$scene_dialog.new_dialog(dialog, force_dialog)
end
#-------------------------------------------------------------------------
# * dialogAction
#-------------------------------------------------------------------------
def self.dialogAction(action)
$scene_dialog.action(action)
end
end
#==============================================================================
# ** Scene_Map_DialogSystem
#------------------------------------------------------------------------------
# klasa pracująca na dialogach, trzymająca okna itp.
#==============================================================================
class Scene_DialogSystem
attr_accessor :dialogProces
#--------------------------------------------------------------------------
# * class_initialize
#--------------------------------------------------------------------------
def initialize
#--set_variables--
@actions = []
@action_index = 0
@action_index_old = nil
@dialog = nil
@choices = nil
@onState = false
@dialogProces = false
@pauseAction = false
#--create_windows--
@msg_window = Win_Msg_Base.new
@msg_window.visible = false
@choice_window = Win_Msg_Choices.new
@choice_window.hide
@command_window = Win_Msg_Command.new(@dialog)
@command_window.hide
end
#--------------------------------------------------------------------------
# * Frame Update
#--------------------------------------------------------------------------
def update
windows_update
input_update
if @onState and thread_need_refresh?
thread_update
end
end
#--------------------------------------------------------------------------
# * dispose
#--------------------------------------------------------------------------
def dispose
@command_window.dispose
@choice_window.dispose
@msg_window.dispose
end
#==========================================================================
#--------------------------------------------------------------------------
# * windows_update
#--------------------------------------------------------------------------
def windows_update
@command_window.update if @command_window.active
@choice_window.update if @choice_window.active
end
#==========================================================================
#--------------------------------------------------------------------------
# * Input_Update
#--------------------------------------------------------------------------
def input_update
if Input.trigger?(Input::C)
inputEnter
end
end
#--------------------------------------------------------------------------
# * Input_Enter
#--------------------------------------------------------------------------
def inputEnter
#--jeżeli--wątek--jest--uruchomiony--i--został--zpauzowany
case dialog_status
when 0 #--thread--select--
runNewThread(selected_thread)
when 1 #--making--choice--
makeChoice
when 2 #--reading--message--
nextAction
end
end
#==========================================================================
#--------------------------------------------------------------------------
# * run_dialog
# dialog: Nazwa_Dialogu + .new - w formie tekstu
# force_dialog: nazwa instancji wątku, który ma zostać wymuszony
#--------------------------------------------------------------------------
def new_dialog(dialog, force_dialog = nil)
@actions = []
@action_index = 0
@action_index_old = nil
@choices = nil
@onState = false
@pauseAction = false
@dialog = eval(dialog+".new")
@dialogProces = true
#--automatyczny_start_wątku--#
if force_dialog != nil
return runNewThread(thread_ByInstance(force_dialog))
end
to_thread_select
end
#--------------------------------------------------------------------------
# * to_thread_select
#--------------------------------------------------------------------------
def to_thread_select
@command_window.show
@command_window.set_dialog(@dialog)
@command_window.index = 0
@choice_window.hide
@msg_window.clear
@msg_window.visible = false
@onState = false
end
#--------------------------------------------------------------------------
# * to_makeing_choice
#--------------------------------------------------------------------------
def to_making_choice
@command_window.hide
@choice_window.show
@choice_window.index = 0
@msg_window.visible = false
end
#--------------------------------------------------------------------------
# * to_dialog_proces
#--------------------------------------------------------------------------
def to_dialog_proces
@command_window.hide
@choice_window.hide
@msg_window.visible = true
end
#--------------------------------------------------------------------------
# * end_dialog
#--------------------------------------------------------------------------
def end_dialog
@actions = []
@action_index = 0
@action_index_old = nil
@dialog = nil
@choices = nil
@onState = false
@dialogProces = false
@pauseAction = false
#--hide_windows--------------------------
@command_window.hide
@choice_window.hide
@msg_window.visible = false
end
#==========================================================================
#--------------------------------------------------------------------------
# * runNewThread
# - przechodzi z wyboru wątku do jego stanu, resetując przy tym zmienne
#--------------------------------------------------------------------------
def runNewThread(thread)
@onState = true
@actions = []
@action_index = 0
@action_index_old = nil
getOnState(thread.onState)
$hero_known_threads.push(thread.instance) if !thread.repeat
end
#--------------------------------------------------------------------------
# * getOnState
# state - state method name
#--------------------------------------------------------------------------
def getOnState(state_name)
eval("@dialog." + state_name)
end
#---------------------------------------------------------------------------
# * nextAction
#---------------------------------------------------------------------------
def nextAction
@action_index += 1
@pauseAction = false
end
#---------------------------------------------------------------------------
# * makeChoice
#---------------------------------------------------------------------------
def makeChoice
@action_index += 1
@pauseAction = false
getOnState(selected_choice)
end
#===========================================================================
#---------------------------------------------------------------------------
#* thread_update
#---------------------------------------------------------------------------
def thread_update
return to_thread_select if @action_index >= @actions.size
eval(@actions[@action_index])
@action_index += 1 if !@pauseAction
end
#---------------------------------------------------------------------------
# * thread_need_refresh?
#---------------------------------------------------------------------------
def thread_need_refresh?
r = false
r = true if @action_index != @action_index_old
#--save_old--index--------------------------------------
@action_index_old = @action_index
#--return--refresh--value-------------------------------
return r
end
#==========================================================================
#--------------------------------------------------------------------------
# * action
#--------------------------------------------------------------------------
def action(action)
@actions.push(action)
end
#==========================================================================
# * actions
#==========================================================================
#--------------------------------------------------------------------------
# * draw_text
#--------------------------------------------------------------------------
def draw_text(text)
to_dialog_proces if dialog_status != 2
@pauseAction = true
@msg_window.refresh(text)
end
#--------------------------------------------------------------------------
# * force_thread
#--------------------------------------------------------------------------
def force_thread(thread_instance_name)
thread = thread_ByInstance(thread_instance_name)
getOnState(thread.onState)
$hero_known_threads.push(thread.instance) if !thread.repeat
end
#--------------------------------------------------------------------------
# * make_choice
#--------------------------------------------------------------------------
def make_choice(choices)
@choices = choices
to_making_choice if dialog_status != 1
@pauseAction = true
@choice_window.rebuild(choice_names)
end
#==========================================================================
# * active_threads
#==========================================================================
#------------------------------------------------------------------------
# * selected_thread - wybrany wątek w command_window
#------------------------------------------------------------------------
def selected_thread
return active_threads[@command_window.index]
end
#------------------------------------------------------------------------
# * thread_ByInstance
#------------------------------------------------------------------------
def thread_ByInstance(instance)
thread = eval("@dialog." + instance)
return thread
end
#------------------------------------------------------------------------
# * active_threads - tablica wątkó dostępnych dla tej postaciw danym momencie
#------------------------------------------------------------------------
def active_threads
return ORG_dialog.return_active_threads(@dialog)
end
#------------------------------------------------------------------------
# * choice names - nazwy wyborów
#------------------------------------------------------------------------
def choice_names
return ORG_choices.choice_names(@choices) if @choices != nil
end
#------------------------------------------------------------------------
# * choice choice_instance_names - nazwy instancji wyborów
#------------------------------------------------------------------------
def choice_instance_names
return ORG_choices.choice_instance_names(@choices) if @choices != nil
end
#------------------------------------------------------------------------
# * selected_choice
#------------------------------------------------------------------------
def selected_choice
instance = choice_instance_names[@choice_window.index]
return instance
end
#===========================================================================
# * dialog_status
#===========================================================================
def dialog_status
if @command_window.active #--thread--select--
return 0
elsif @choice_window.active #--making--choice--
return 1
elsif @msg_window.visible #--dialog_proces--
return 2
end
end
end
#==============================================================================
# ** window_message_command
# okno wyboru w wątku w dialogu
#------------------------------------------------------------------------------
class Win_Msg_Command < Window_Command
#--------------------------------------------------------------------------
# * Object Initialization
#--------------------------------------------------------------------------
def initialize(dialog)
super(580, [""])
self.contents = Bitmap.new(width - 32, height - 32)
self.x = 30
self.y = 320
self.height = 160
self.z = 12
self.opacity = 160
@dialog = dialog
rebuild
end
#--------------------------------------------------------------------------
# * rebuild
#--------------------------------------------------------------------------
def rebuild
return if @dialog == nil
@item_max = ORG_dialog.return_active_thread_names(@dialog).size
@commands = ORG_dialog.return_active_thread_names(@dialog)
self.contents = Bitmap.new(width - 32, @item_max * 32)
refresh
end
#--------------------------------------------------------------------------
# * set_dialog
#--------------------------------------------------------------------------
def set_dialog(dialog)
@dialog = dialog
rebuild
end
#--------------------------------------------------------------------------
# * hide
#--------------------------------------------------------------------------
def hide
self.active = false
self.visible = false
end
#--------------------------------------------------------------------------
# * show
#_--------------------------------------------------------------------------
def show
self.active = true
self.visible = true
end
end
#==============================================================================
# ** window_message_choices
# okno wyboru w wątku w dialogu 2
#------------------------------------------------------------------------------
class Win_Msg_Choices < Window_Command
#--------------------------------------------------------------------------
# * Object Initialization
#--------------------------------------------------------------------------
def initialize
super(580, [""])
self.contents = Bitmap.new(width - 32, height - 32)
self.x = 30
self.y = 320
self.height = 160
self.z = 11
self.opacity = 160
@choice_names = nil
hide
end
#--------------------------------------------------------------------------
# * rebuild
#--------------------------------------------------------------------------
def rebuild(choice_names)
@choice_names = choice_names
return if @choice_names == nil
@item_max = @choice_names.size
@commands = @choice_names
self.contents = Bitmap.new(width - 32, @item_max * 32)
refresh
end
#--------------------------------------------------------------------------
# * hide
#--------------------------------------------------------------------------
def hide
self.active = false
self.visible = false
end
#--------------------------------------------------------------------------
# * show
#_--------------------------------------------------------------------------
def show
self.active = true
self.visible = true
end
end
#==============================================================================
# ** window_message_base
# bazowe okno wiadomośći
#------------------------------------------------------------------------------
class Win_Msg_Base < Window_Base
#--------------------------------------------------------------------------
# * Object Initialization
#--------------------------------------------------------------------------
def initialize
super(30, 320, 580, 160)
self.contents = Bitmap.new(width - 32, height - 32)
refresh("")
self.z = 10
self.opacity = 160
end
#--------------------------------------------------------------------------
# * refresh
#--------------------------------------------------------------------------
def refresh(text)
self.contents.clear
paragraph = str_paragraph(text, 548)
draw_paragraph(0, 0, 548 , 24, paragraph)
end
#--------------------------------------------------------------------------
# * clear
#--------------------------------------------------------------------------
def clear
self.contents.clear
end
end
#===============================================================================
# ORG_threads - moduł przeprowadza operacje na obiektach Thread.new
#===============================================================================
module ORG_threads
public
#=============================================================================
# **hero_know_thread
# -sprawdza czy gracz zna już dany dialog
#=============================================================================
def self.hero_know_thread(thread)
for i in 0...$hero_known_threads.size
if thread.instance == $hero_known_threads[i]
return true
end
end
return false
end
end
#===============================================================================
# ORG_choices - moduł przeprowadze operacje na tablicach z kwestiami wyboru
#===============================================================================
module ORG_choices
public
#=============================================================================
# **choice_names
# zwraca tablicę z nazwami wyborów
#=============================================================================
def self.choice_names(choice)
#create_array--------------------------------
choice_names = []
#fill_array----------------------------------
for i in 0...choice.size
choice_names.push(choice[i][0])
end
#return_array--------------------------------
return choice_names
end
#=============================================================================
# **choice_instance_names
# zwraca tablicę z nazwami instancji wyborów
#=============================================================================
def self.choice_instance_names(choice)
#create_array--------------------------------
choice_instance_names = []
#fill_array----------------------------------
for i in 0...choice.size
choice_instance_names.push(choice[i][1])
end
#return_array--------------------------------
return choice_instance_names
end
end
#===============================================================================
# ORG_dialog - obiekt przeprowadza operacje na plikach z dialogami(klasach)
#===============================================================================
module ORG_dialog
public
#========================================================================
# * all_threads_in_dialog
#========================================================================
#------------------------------------------------------------------------
# * return_thread_instance_names
# :zwraca tablicę nawzw instacji
# dialog: klasa dialogu dla konkretnej postacji
#------------------------------------------------------------------------
def self.return_thread_instance_names(dialog)
return dialog.dia
end
#------------------------------------------------------------------------
# * return_thread_objects obiekty Thread.new
#------------------------------------------------------------------------
def self.return_thread_objects(dialog)
#--create--array-------------------------------------------------
threads = []
thread_instance_names = return_thread_instance_names(dialog)
#--pętla---------------------------------------------------------
for i in 0...thread_instance_names.size
threads.push(eval("dialog." + thread_instance_names[i]))
end
#--return--obects--array-----------------------------------------
return threads
end
#------------------------------------------------------------------------
# * return_thread_names
#------------------------------------------------------------------------
def self.return_thread_names(dialog)
#--create--array-------------------------------------------------
thread_names = []
threads = return_thread_objects(dialog)
#--pętla---------------------------------------------------------
for i in 0...threads.size
thread_names.push(threads[i].name)
end
#--return--obects--array-----------------------------------------
return thread_names
end
#========================================================================
# * only_active_threads
#========================================================================
#------------------------------------------------------------------------
# * return_active_thread
# zwraca jedynie aktywne wątki - obiekty Thread.new
#------------------------------------------------------------------------
def self.return_active_threads(dialog)
#--create--array-------------------------------------------------
active_threads = []
threads = return_thread_objects(dialog)
#--pętla---------------------------------------------------------
for i in 0...threads.size
if eval("dialog." + threads[i].active) and !ORG_threads.hero_know_thread(threads[i])
active_threads.push(threads[i])
end
end
#--return--obects--array-----------------------------------------
return active_threads
end
#------------------------------------------------------------------------
# * return_active_thread_instance_names
#------------------------------------------------------------------------
def self.return_active_thread_instance_names(dialog)
#--create--array-------------------------------------------------
active_thread_instance_names = []
active_threads = return_active_threads(dialog)
#--pętla---------------------------------------------------------
for i in 0...active_threads.size
active_thread_instance_names.push(active_threads[i].instance)
end
#--return--obects--array-----------------------------------------
return active_thread_instance_names
end
#------------------------------------------------------------------------
# * return_active_thread_names
#------------------------------------------------------------------------
def self.return_active_thread_names(dialog)
#--create--array-------------------------------------------------
active_thread_names = []
active_threads = return_active_threads(dialog)
#--pętla---------------------------------------------------------
for i in 0...active_threads.size
active_thread_names.push(active_threads[i].name)
end
#--return--obects--array-----------------------------------------
return active_thread_names
end
end
ATP
Kod:
=begin
ATP(Advanced Text Paragrapher) V1.0 by Samo, The thief
Ok, Something of The Scripters do normally to draw a text in form of a paragraph is
doing an array [] that contains each line. The Time Has come For
This Microsoft Word Effect!
This Script Just need a Long String and it will paragraph it!
How to call it?
paragraph = str_paragraph(string, width of the paragraph.)
Example :
@my_paragraph = str_paragraph("La la la la la la la la la , This is a Looooong Strriiiing!", 120)
Returns an Array with each line separately.
How to draw it?
draw_paragraph(x,y,width,height, paragraph)
width and height for each line, not for the paragraph.
.::-NOTE-::.
If you put a ' ^ '(must be between spaces), the text will pass to the next line.
This Symbol Won't be drawed.
Reminder: Always use '' instead of ""! It works faster!
=end
class Window_Base < Window
#--------------------------------------------------
def str_paragraph(str_old, width)
temp_str = ''
str = '' + str_old
words = []
size = 0
str_size = 0
#
while ((c = str.slice!(/./m)) != nil)
temp_str += c
str_size += 1
if c == ' '
words.push(temp_str)
temp_str = ''
end
if str.size == 0
words.push(temp_str)
temp_str = ''
end
end
lines = []
for i in 0...words.size
word = words[i]
if word == '^ '
lines.push(temp_str)
temp_str = ''
next
end
temp_str += word
size = contents.text_size(temp_str).width
if size > width - contents.text_size(' ').width
for i in 1..word.size
temp_str = temp_str.chop
end
lines.push(temp_str)
temp_str = ''
temp_str += word
end
end
words = words.compact
if temp_str != ''
lines.push(temp_str)
end
return lines
end
#---------------------------------------------------------------------
def draw_paragraph(x,y,width,height,lines,align = 0, start_line = 0)
for i in start_line...lines.size
self.contents.draw_text(x, y + (i - start_line) * self.contents.font.size + 1, width, height, lines[i], align)
end
end
#-----------------------------------------------------------------
end
Screen:
Demo:
Pełne(razem z plikami dialogów, postaciami itp.)
http://www.mediafire.com/download/hkbx6nwny9fg6js/BDS.rar
https://drive.google.com/file/d/0B5AhCxW3O7X-ZGlvWDZla05xUzg/edit?usp=sharing
Czyste(bez dialogów, jedynie sam skrypt)
http://www.mediafire.com/download/gr9r19cdk964262/BDS(czysty).rar
https://drive.google.com/file/d/0B5AhCxW3O7X-QlFIQllrcWx4Mms/edit?usp=sharing
Od autora:
Być może nieco chaotycznie wszystko opisałem, ale jakoś się może połapiecie. Strona skryptu nie jest jeszcze całkiem skończona(ale robiłem ją z trzy razy dłużej niż sam skrypt i już miałem dosyć, więc udostępniam). Mam nadzieję tylko, że BDS komuś się przyda :) Ja sam również z niego będę korzystał, ciągle pracuję nad "Baśnią o Grimie" :P nie wiem czy ktoś jeszcze to pamięta :P
Można go edytować itp. itd. udostępniać również, byle zaznaczyć, że ja jestem pierwszym autorem :)
Jeszcze raz wspomnę, większość informacji znajdziecie na stronie, pisanie ich tutaj mijało by się z celem ;-)
RE: BDS(Beo117 Dialog System) - helio108 - 08-02-14 10:17
Dodasz więcej SS? Te co dodałeś pokazują że okno jest mniejsze :P
RE: BDS(Beo117 Dialog System) - beo117 - 08-02-14 10:45
Mogę :P Ale nic więcej nie da się na nich pokazać :) To jest skrypt czysto techniczny, trzeba go przetestować, zagrać w demo żeby zrozumieć jak to działa. To okno, które tu widać to jedyny "widoczny" element skryptu.
RE: BDS(Beo117 Dialog System) - ~Nakhitto - 08-05-14 17:21
Ciekawy skrypcik :D Gdyby nie trochę długie bawienie się z dialogiem to pewnie bym skorzystał.
|