- Joined
- Jun 26, 2020
- Messages
- 1,853
JASS version
Lua version:
vJASS:
library Transmission /* version 2.2.0
*/requires /*
*/TimerUtils, /*
*/Alloc, /*
*/ArrayList, /*
*/optional StoreUnitColor
//! novjass
//How it works:
// Set a value of type Transmission and use the function create like this:
local Transmission curr = Transmission.create()
// or
local Transmission curr = Transmission.CreateEx(force toForce, integer data)
// also you can set the target force of the transmission using:
call curr.SetTargetForce(force toForce) //This not assing a force, just move the players of the input to the actually target force, so using a function that creates a force
//in the argument toForce will cause an object leak.
// you can also save an integer to use during the transmission using:
call curr.Data = data /*To recover the data use:*/ curr.Data
// if you wanna add or remove a player or a force to the target force just use the normal functions ForceAddPlayer, ForceRemovePlayer and the added ForceAddForce, ForceRemoveForce
// To reduce the process use:
local Transmission curr = Transmission.CreateEx(toForce,data)
// Then to add a dialogue line just do:
call curr.AddLine(unit whichUnit, playercolor color, string unitName, sound soundHandle, string message, integer timeType, real timeVal, boolean wait)
"whichUnit": The unit who do the dialogue line.
"color": The color of the unit in the dialogue (if you set this value to null so then, if you have the StoreUnitColor library the unit will have their actual color, if not, it will have their owner color).
"unitName": The name of that unit.
"soundHandle": The sound that will be played during the dialogue line.
"message": what the unit will say.
"timeType": Is like the GUI function, you can use 0 (Transmission.ADD), 1 (Transmission.SET), 2 (Transmission.SUB), another value will make the wait time to 0.
"timeVal": Is like the GUI function, the normal duration of the transmission is the duration of the sound and you can edit the value depending of this and the previous value.
"wait": Is like the GUI function, if this is true the next transmission will be wait the time depending of the previous two values.
/* In case you don't wanna set a unit, but instead a unit-type you can use */curr.AddLinebyId/*instead (default color BLACK)*/
// Also you can add actions between the lines using the function:
call curr.AddAction(code actions, real delay)
"actions": The function that will run.
"delay": If you wanna add a delay to the next action or line.
// If you wanna the action run in the exact moment a line is happening just set the delay to 0.00 and put it right before the line.
/* If you wanna just add an extra delay just put */null/* in the actions part*/
// I have to say this steps are assinged an index by the order they were declared that is the number of the step.
// You can also add an action to the end of the transmission (this function will run yes or yes even you skip the transmission) using:
call curr.AddEnd(code func)
/* Where you can use the values */set curr=Transmission.instance/*to reffer to the actual instance, and with that use */curr.Data/* if you stored one and */curr.Skipped/* to detect
if the cinematic was skipped but if was skipped in the last line this value will remain false.*/
// After set the lines call the function
call curr.Start()
// To start the transmission, and to skip the transmission (the end function will run anyway) just press the ESC button,
// (this will skip all the transmissions where the player is) but if the implied force has more than one player the process will continue
// to the rest of players on that force (.toForce is empty). The transmission will end only if all the implied players skip the transmission (So if you use "All players"
/* probably you never won't skip the transmission) but you can use the */function Force(player p)/* to have a force just for 1 player (Don't destroy those forces).*/
// If you wanna you can also pause the transmission using:
call curr.Stop(boolean fadeout) //For the actual played sound.
// or:
call curr.StopUntil(boolean fadeout, real delay) //If you wanna the transmission resume itself after a while
// And to resume manually use:
call curr.Resume()
// Note: The transsmision and its elements are not stored after start it, they are recycled once the transmission ends.
// Other members:
force toForce, to reffer the force to which it is addressed the transmission.
force OriginalTargetForce, if you edited the force toForce, you can have the original force with this.
player toPlayer, if the transmission is just for one player you can use this instead, in other case this will return the last added player. //(I don't know the order)
integer LineNumber, is the number of the line that is running.
integer ActionNumber, is the number of the action that is running.
integer StepNumber, is the number of the step that is running (line or actions).
boolean InUse, returns if the instance is allocated.
boolean Paused, returns if the transmission is paused.
TransmissionElements Elements, to reffer to the elements instance of the actual step.
method AddSceneActions takes code func returns nothing
/*If you wanna add an action that runs everytime an step (line or action except the end) is runned, this also can be a replace of .AddAction in some
moments if you compare the number of the step with StepNumber, to know if is a line or action use the member boolean IsLine
and if is true you can use the member integer LineNumber to know the number of the actual line.
This also runs before the action or line is runned, so you can use it to edit the current step.*/
method RemoveSceneActions takes triggeraction actions returns nothing
//This remove the actions that you added before.
//If you wanna set the elements by separated you can use the member
method AddStep takes string whattype returns TransmissionElements
/*This add an step that also have an index by the order is placed, the input only can be*/"line"/* and */"action"
method InsertStep takes integer stepnumber, string whattype returns TransmissionElements
/*This add an step but the index is set manually, if there are steps that were set after this and after the transmission was started
(including that was had that stepnumber) those index will be increased by 1 to not overwrite.*/
method RemoveStep takes integer stepnumber returns nothing
//This remove an step using the number of the step, in this case the stepnumber of the next steps will be decreased by 1.
//After add the step the returned value have their own members
unit Unit //This is the unit who will be marked during the transmission (not who will apear in the transmission).
integer UnitType
playercolor Color
string Name
sound Sound
string Message
real Time
integer TimeType
boolean WillWait
boolean Delay
integer NumberLine //Please don't edit this value
integer NumberAction //Please don't edit this value
integer NumberStep //Please don't edit this value
method SetActions takes code func returns nothing //This replace all the actions
method AddActions takes code func returns nothing //This add actions without affecting the added actions
method GetActions takes nothing returns trigger
//But if you wanna use this members using the number of the step use their "ByIndex" methods (these are members of Transmission not of TransmissionElements)
//You have to do:
call curr.Set$Name of the member$ByIndex(integer stepnumber, $type of the member$ value)
set value = curr.Get$Name of the member$ByIndex(integer stepnumber) (Except WillWait and AddActions)
//Example:
local Transmission curr = Transmission.create()
call curr.AddStep("line") //is the step 1
call curr.AddStep("action") //is the step 2
call curr.SetUnitByIndex(1,<your unit>)
call curr.SetActionsByIndex(2,<your actions>)
//A replace of:
local Transmission curr = Transmission.create()
local TransmissionElements what
set what=curr.AddStep("line")
call what.SetUnit(<your unit>)
set what=curr.AddStep("action")
call what.SetActions(<your actions>)
// Here are more members:
IntegerList Lines
IntegerList Actions
IntegerList Steps
integer LineNumber // Same as Transmission.instance.Elements.NumberLine
integer ActionNumber // Same as Transmission.instance.Elements.NumberAction
integer StepNumber // Same as Transmission.instance.Elements.NumberStep
boolean IsLine
boolean InUse // If is allocated
boolean Paused
// But if you wanna save the number of lines you can set default values (before than create the steps)
unit DefUnit
integer DefUnitType
playercolor DefColor
string DefName
sound DefSound
string DefMessage
real DefTime
integer DefTimeType
boolean DefWillWait
boolean DefDelay
//! endnovjass
//A function to use
globals
private IntegerList array AllInstances
private integer Temp
private force array What_Force
private force Other
endglobals
private function Enum takes nothing returns nothing
set Temp = Temp + 1
endfunction
function IsForceEmpty takes force f returns boolean
if f == null then
return true
else
set Temp = 0
call ForForce(f,function Enum)
endif
return Temp == 0
endfunction
private function ForceAddForceEnum takes nothing returns nothing
call ForceAddPlayer(Other, GetEnumPlayer())
endfunction
function ForceAddForce takes force principal, force other returns nothing
if principal == null or other == null then
return
else
set Other = other
call ForForce(principal, function ForceAddForceEnum)
endif
endfunction
private function ForceRemoveForceEnum takes nothing returns nothing
call ForceRemovePlayer(Other, GetEnumPlayer())
endfunction
function ForceRemoveForce takes force principal, force other returns nothing
if principal == null or other == null then
return
else
set Other = other
call ForForce(principal,function ForceRemoveForceEnum)
endif
endfunction
function Force takes player p returns force
return What_Force[GetPlayerId(p)]
endfunction
// This are the elements of each line transmission
struct TransmissionElements extends array
implement Alloc
boolean isline
private unit talker
private integer talkerid
private playercolor color
private string name
private real duration
private integer timetype
private boolean wait
private sound mysound
private string text
private trigger actions
integer NumberLine
integer NumberAction
integer NumberStep
method destroy takes nothing returns nothing
set this.talker = null
set this.name = null
set this.mysound = null
set this.text = null
set this.timetype = -1
set this.duration = 0.00
set this.wait = false
set this.isline = false
if this.actions != null then
call TriggerClearActions(this.actions)
call DestroyTrigger(this.actions)
set this.actions = null
endif
set this.NumberLine = 0
set this.NumberAction = 0
set this.NumberStep = 0
call this.deallocate()
endmethod
// To set the values
method operator Unit= takes unit whichUnit returns nothing
if not this.isline then
debug call BJDebugMsg("This is an action |cffff0000Unit=|r")
return
endif
set this.talker = whichUnit
endmethod
method operator UnitType= takes integer id returns nothing
if not this.isline then
debug call BJDebugMsg("This is an action |cffff0000UnitType=|r")
return
endif
set this.talkerid = id
endmethod
method operator Color= takes playercolor whichColor returns nothing
if not this.isline then
debug call BJDebugMsg("This is an action |cffff0000Color=|r")
return
endif
set this.color = whichColor
endmethod
method operator Name= takes string unitName returns nothing
if not this.isline then
debug call BJDebugMsg("This is an action |cffff0000Name=|r")
return
endif
set this.name = unitName
endmethod
method operator Sound= takes sound soundHandle returns nothing
if not this.isline then
debug call BJDebugMsg("This is an action |cffff0000Sound=|r")
return
endif
set this.mysound = soundHandle
endmethod
method operator Message= takes string message returns nothing
if not this.isline then
debug call BJDebugMsg("This is an action |cffff0000Message=|r")
return
endif
set this.text = message
endmethod
method operator TimeType= takes integer timeType returns nothing
if not this.isline then
debug call BJDebugMsg("This is an action |cffff0000TimeType=|r")
return
endif
set this.timetype = timeType
endmethod
method operator Time= takes real timeVal returns nothing
if not this.isline then
debug call BJDebugMsg("This is an action |cffff0000Time=|r")
return
endif
set this.duration = timeVal
endmethod
method operator WillWait= takes boolean wait returns nothing
if not this.isline then
debug call BJDebugMsg("This is an action |cffff0000WillWait=|r")
return
endif
set this.wait = wait
endmethod
method operator Delay= takes real delay returns nothing
if this.isline then
debug call BJDebugMsg("This is a line |cffff0000Delay=|r")
return
endif
set this.duration = delay
endmethod
method SetActions takes code func returns nothing
if this.isline then
debug call BJDebugMsg("This is a line |cffff0000SetActions|r")
return
endif
if this.actions == null then
set this.actions = CreateTrigger()
else
call TriggerClearActions(this.actions)
endif
if func != null then
call TriggerAddAction(this.actions,func)
endif
endmethod
method AddActions takes code func returns nothing
if this.isline then
debug call BJDebugMsg("This is a line |cffff0000AddActions|r")
return
//Only add actions if func is not null and this.actions was created previously
elseif func == null then
return
elseif this.actions == null then
debug call BJDebugMsg("Why do you wanna \"add\" actions if there isn't even a trigger with actions?")
return
else
call TriggerAddAction(this.actions,func)
endif
endmethod
//To get the values
method operator Unit takes nothing returns unit
if not this.isline then
debug call BJDebugMsg("This is an action |cffff0000Unit|r")
return null
endif
return this.talker
endmethod
method operator UnitType takes nothing returns integer
if not this.isline then
debug call BJDebugMsg("This is an action |cffff0000UnitType|r")
return 0
endif
return this.talkerid
endmethod
method operator Color takes nothing returns playercolor
if not this.isline then
debug call BJDebugMsg("This is an action |cffff0000Color|r")
return null
endif
return this.color
endmethod
method operator Name takes nothing returns string
if not this.isline then
debug call BJDebugMsg("This is an action |cffff0000Name|r")
return null
endif
return this.name
endmethod
method operator Sound takes nothing returns sound
if not this.isline then
debug call BJDebugMsg("This is an action |cffff0000Sound|r")
return null
endif
return this.mysound
endmethod
method operator Message takes nothing returns string
if not this.isline then
debug call BJDebugMsg("This is an action |cffff0000Message|r")
return null
endif
return this.text
endmethod
method operator TimeType takes nothing returns integer
if not this.isline then
debug call BJDebugMsg("This is an action |cffff0000TimeType|r")
return 0
endif
return this.timetype
endmethod
method operator Time takes nothing returns real
if not this.isline then
debug call BJDebugMsg("This is an action |cffff0000Time|r")
return 0.00
endif
return this.duration
endmethod
method operator WillWait takes nothing returns boolean
if not this.isline then
debug call BJDebugMsg("This is an action |cffff0000WillWait|r")
return false
endif
return this.wait
endmethod
method operator Delay takes nothing returns real
if this.isline then
debug call BJDebugMsg("This is a line |cffff0000Delay|r")
return 0.00
endif
return this.duration
endmethod
method GetActions takes nothing returns trigger
if this.isline then
debug call BJDebugMsg("This is a line |cffff0000GetActions|r")
return null
endif
return this.actions
endmethod
// The methods that create the elements
static method create takes string whattype returns thistype
local thistype this = thistype.allocate()
if whattype == "line" then
set this.isline = true
elseif whattype == "action" then
set this.isline = false
else
debug call BJDebugMsg("Invalid type.")
call this.deallocate()
return 0
endif
return this
endmethod
static method create_action takes code func, real delay returns thistype
local thistype this = thistype.create("action")
// Since I can't store codes I use triggers
if func!=null then
set this.actions = CreateTrigger()
call TriggerAddAction(this.actions,func)
endif
set this.duration = delay
return this
endmethod
static method create_line takes unit whichUnit, playercolor whichColor, string unitName, sound soundHandle, string message, integer timeType, real timeVal, boolean wait returns thistype
local thistype this = thistype.create("line")
set this.talker = whichUnit
set this.name = unitName
set this.mysound = soundHandle
set this.text = message
set this.timetype = timeType
set this.duration = timeVal
set this.wait = wait
set this.isline = true
if whichUnit == null then
set this.talkerid = 0
if whichColor == null then
static if LIBRARY_StoreUnitColor then
set this.color = PLAYER_COLOR_BLACK
else
set this.color = ConvertPlayerColor(PLAYER_NEUTRAL_AGGRESSIVE)
endif
else
set this.color = whichColor
endif
else
set this.talkerid = GetUnitTypeId(whichUnit)
if whichColor == null then
static if LIBRARY_StoreUnitColor then
set this.color = GetUnitColor(whichUnit)
else
set this.color = GetPlayerColor(GetOwningPlayer(whichUnit))
endif
else
set this.color = whichColor
endif
endif
return this
endmethod
endstruct
/*****************************************************/
/********The actual struct of the transmission********/
/*****************************************************/
private module M
// These are to make it more elegant than just put integers, but you can use the integers if you want
static constant integer ADD = bj_TIMETYPE_ADD // 0
static constant integer SET = bj_TIMETYPE_SET // 1
static constant integer SUB = bj_TIMETYPE_SUB // 2
readonly static player LocalPlayer
private static integer array Index
private static integer array Index_current
readonly static thistype instance
readonly player toPlayer
readonly force toForce
readonly force OriginalTargetForce
readonly IntegerList Lines
readonly IntegerList Actions
readonly IntegerList Steps
unit DefUnit
integer DefUnitType
playercolor DefColor
string DefName
sound DefSound
string DefMessage
real DefTime
integer DefTimeType
boolean DefWillWait
real DefDelay
private boolean allocated
private integer data
private boolean paused
private boolean skipped
private sound played
private trigger final
private timer t
private TransmissionElements elements
private integer current
private integer actual_line
private integer actual_action
private integer actual_step
private boolean ended
private trigger scene
private method Invalid takes string func returns boolean
local boolean b = this<=0 or not this.allocated
static if DEBUG_MODE then
if b then
call BJDebugMsg("Invalid instance |cffff0000"+func+"|r")
endif
endif
return b
endmethod
// I use operators to display the error message everytime this values are called
method operator LineNumber takes nothing returns integer
call this.Invalid("LineNumber")
if not this.elements.isline then
debug call BJDebugMsg("This step is an action |cffff0000LineNumber|r")
return 0
endif
return this.elements.NumberLine
endmethod
method operator ActionNumber takes nothing returns integer
call this.Invalid("ActionNumber")
if this.elements.isline then
debug call BJDebugMsg("This step is an line |cffff0000ActionNumber|r")
return 0
endif
return this.elements.NumberAction
endmethod
method operator StepNumber takes nothing returns integer
call this.Invalid("StepNumber")
if this.ended then
return this.actual_step+1
endif
return this.elements.NumberStep
endmethod
method operator IsLine takes nothing returns boolean
return this.elements.isline
endmethod
method operator InUse takes nothing returns boolean
call this.Invalid("InUse")
return this.allocated
endmethod
method operator Data= takes integer data returns nothing
if this.Invalid("Data") then
return
endif
set this.data = data
endmethod
method operator Data takes nothing returns integer
call this.Invalid("Data")
return this.data
endmethod
method operator Paused takes nothing returns boolean
call this.Invalid("Paused")
return this.paused
endmethod
method operator Skipped takes nothing returns boolean
call this.Invalid("Skipped")
return this.skipped
endmethod
method operator Elements takes nothing returns TransmissionElements
call this.Invalid("Elements")
return this.elements
endmethod
// When the transmission ends
private static method ClearData takes nothing returns nothing
local integer i = GetPlayerId(GetEnumPlayer())
local integer j = 1
loop
exitwhen j > AllInstances[i].length
if thistype.instance == AllInstances[i][j] then
call AllInstances[i].removeFrom(j)
exitwhen true
endif
set j = j + 1
endloop
endmethod
private method finish takes nothing returns nothing
call this.Steps.destroy()
call this.Lines.destroy()
call this.Actions.destroy()
call ReleaseTimer(this.t)
set this.ended = true
set thistype.instance = this
if this.final != null then
call TriggerExecute(this.final)
call TriggerClearActions(this.final)
call DestroyTrigger(this.final)
set this.final = null
endif
call ForForce(this.toForce, function thistype.ClearData)
set thistype.instance = 0
call DestroyForce(this.OriginalTargetForce)
call DestroyForce(this.toForce)
call TriggerClearActions(this.scene)
call DestroyTrigger(this.scene)
set this.elements = 0
set this.scene = null
set this.data = 0
set this.played = null
set this.toForce = null
set this.toPlayer = null
set this.t = null
set this.allocated = false
call this.deallocate()
endmethod
private method what_call takes nothing returns nothing
set this.current = this.current + 1
set this.elements = this.Steps[this.current]
if this.elements == 0 or IsForceEmpty(this.toForce) then
// If the cinematic was skipped just in the last line it won't be counted as skipped
set this.skipped = IsForceEmpty(this.toForce) and this.elements != 0
call this.finish()
else
if this.elements.isline then
call this.cinematic_line()
else
call this.cinematic_actions()
endif
endif
endmethod
private static method callback takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
call this.what_call()
endmethod
//Where the magic happens
private method cinematic_line takes nothing returns nothing
local integer alpha = 0
local real delay = 0.00
local TransmissionElements what = this.elements
if this.scene != null then
set thistype.instance = this
call TriggerExecute(this.scene)
set thistype.instance = 0
endif
set this.played = what.Sound
set bj_lastTransmissionDuration = GetTransmissionDuration(this.played, what.TimeType, what.Time)
if IsPlayerInForce(thistype.LocalPlayer,this.toForce) then
if this.played != null then
call StartSound(this.played)
endif
call SetCinematicScene(what.UnitType, what.Color, what.Name, what.Message, bj_lastTransmissionDuration + bj_TRANSMISSION_PORT_HANGTIME, bj_lastTransmissionDuration)
set alpha = 255
endif
if what.Unit != null then
call UnitAddIndicator(what.Unit, 255, 255, 255, alpha)
endif
if what.WillWait then
set delay = bj_lastTransmissionDuration
endif
if delay > 0.00 then
call TimerStart(this.t, delay, false, function thistype.callback)
else
call this.what_call()
endif
call what.destroy()
endmethod
private method cinematic_actions takes nothing returns nothing
local TransmissionElements what = this.elements
set thistype.instance = this
if this.scene != null then
call TriggerExecute(this.scene)
endif
if what.GetActions() != null then
call TriggerExecute(what.GetActions())
endif
set thistype.instance = 0
if what.Delay > 0.00 then
call TimerStart(this.t, what.Delay, false, function thistype.callback)
else
call this.what_call()
endif
call what.destroy()
endmethod
//If the instance is 0 or a not allocated then none of this methods will run
method Resume takes nothing returns nothing
if this.Invalid("Resume") then
return
endif
set this.paused = false
call ResumeTimer(this.t)
if IsPlayerInForce(thistype.LocalPlayer, this.toForce) then
call StartSound(this.played)
endif
call SetSoundOffsetBJ(TimerGetElapsed(this.t), this.played) //In case of desync
endmethod
method Stop takes boolean fadeout returns nothing
if this.Invalid("Stop") then
return
endif
set this.paused = true
call PauseTimer(this.t)
call StopSound(this.played, false, fadeout)
endmethod
private static method NowResume takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
call this.Resume()
call ReleaseTimer(GetExpiredTimer())
endmethod
method StopUntil takes boolean fadeout, real delay returns nothing
if this.Invalid("StopUntil") then
return
endif
call this.Stop(fadeout)
if delay > 0.00 then
call TimerStart(NewTimerEx(this), delay, false, function thistype.NowResume)
else
call this.Resume()
debug call BJDebugMsg("Why do you even use this method?, Ay.")
endif
endmethod
method Start takes nothing returns nothing
if this.Invalid("Start") then
return
endif
//If there is not a line or action so it just go to the end
set this.current = 0
set this.elements = 0
call this.what_call()
endmethod
//Since I can't store codes I use triggers
method AddEnd takes code func returns nothing
if this.Invalid("AddEnd") then
return
endif
if this.final == null then
set this.final = CreateTrigger()
endif
call TriggerAddAction(this.final,func)
endmethod
//To add an action everytime a line or action is runned
method AddSceneActions takes code func returns triggeraction
if this.Invalid("AddSceneActions") then
return null
endif
if this.scene == null then
set this.scene = CreateTrigger()
endif
return TriggerAddAction(this.scene, func)
endmethod
//But if you wanna remove it
method RemoveSceneActions takes triggeraction actions returns nothing
if this.Invalid("RemoveSceneActions") then
return
endif
call TriggerRemoveAction(this.scene,actions)
endmethod
//Methods to change the values individually
private method LoadStep takes integer stepnumber returns TransmissionElements
return this.Steps[stepnumber]
endmethod
private method LoadLine takes integer linenumber returns TransmissionElements
return this.Lines[linenumber]
endmethod
private method LoadAction takes integer actionnumber returns TransmissionElements
return this.Actions[actionnumber]
endmethod
//! textmacro SetGetValues takes FUNC1,FUNC2,TYPE,OPERATOR,ERROR1,ERROR2
method $FUNC1$ByIndex takes integer stepnumber, $TYPE$ value returns nothing
call this.Invalid($ERROR1$)
set this.LoadStep(stepnumber).$OPERATOR$ = value
endmethod
method $FUNC2$ByIndex takes integer stepnumber returns $TYPE$
call this.Invalid($ERROR2$)
return this.LoadStep(stepnumber).$OPERATOR$
endmethod
//! endtextmacro
//! runtextmacro SetGetValues("SetUnit","GetUnit","unit","Unit","\"SetUnit\"","\"GetUnit\"")
//! runtextmacro SetGetValues("SetUnitType","GetUnitType","integer","UnitType","\"SetUnitType\"","\"GetUnitType\"")
//! runtextmacro SetGetValues("SetColor","GetColor","playercolor","Color","\"SetColor\"","\"GetColor\"")
//! runtextmacro SetGetValues("SetName","GetName","string","Name","\"SetName\"","\"GetName\"")
//! runtextmacro SetGetValues("SetSound","GetSound","sound","Sound","\"SetSound\"","\"GetSound\"")
//! runtextmacro SetGetValues("SetMessage","GetMessage","string","Message","\"SetMessage\"","\"GetMessage\"")
//! runtextmacro SetGetValues("SetTimeType","GetTimeType","integer","TimeType","\"SetTimeType\"","\"GetTimeType\"")
//! runtextmacro SetGetValues("SetTime","GetTime","real","Time","\"SetTime\"","\"GetTime\"")
//! runtextmacro SetGetValues("SetWillWait","WillWait","boolean","WillWait","\"SetWillWait\"","\"WillWait\"")
//! runtextmacro SetGetValues("SetDelay","GetDelay","real","Delay","\"SetDelay\"","\"GetDelay\"")
method SetActionsByIndex takes integer stepnumber, code func returns nothing
call this.Invalid("SetActionsByIndex")
call this.LoadStep(stepnumber).SetActions(func)
endmethod
method GetActionsByIndex takes integer stepnumber returns trigger
call this.Invalid("GetActionsByIndex")
return this.LoadStep(stepnumber).GetActions()
endmethod
method AddActionsByIndex takes integer stepnumber, code func returns nothing
call this.Invalid("AddMoreActionsByIndex")
call this.LoadStep(stepnumber).AddActions(func)
endmethod
//If you wanna remove a step
method RemoveStep takes integer stepnumber returns nothing
local TransmissionElements what = this.Steps.removeFrom(stepnumber)
local integer j
if what.isline then
set j = 1
loop
exitwhen j > this.Lines.length
if what == this.Lines[j] then
call this.Lines.removeFrom(j)
exitwhen true
endif
set j = j + 1
endloop
set this.actual_line = this.Lines.length
set j = stepnumber
loop
exitwhen j > this.actual_line
set this.LoadLine(j).NumberLine = this.LoadLine(j).NumberLine - 1
set j = j + 1
endloop
else
set j = 1
loop
exitwhen j > this.Actions.length
if what == this.Actions[j] then
call this.Actions.removeFrom(j)
exitwhen true
endif
set j = j + 1
endloop
set this.actual_action = this.Actions.length
set j = stepnumber
loop
exitwhen j > this.actual_action
set this.LoadAction(j).NumberAction = this.LoadAction(j).NumberAction - 1
set j = j + 1
endloop
endif
set this.actual_step = this.Steps.length
set j = stepnumber
loop
exitwhen j > this.actual_step
set this.LoadStep(j).NumberStep = this.LoadStep(j).NumberStep - 1
set j = j + 1
endloop
call what.destroy()
endmethod
//If you wanna add a step in the middle of the steps list
method InsertStep takes string whattype, integer stepnumber returns TransmissionElements
local TransmissionElements node = TransmissionElements.create(whattype)
local integer i
local integer j
if node == 0 then
return 0
endif
if whattype=="line" then
set node.NumberLine = this.actual_line
set node.Unit = this.DefUnit
set node.UnitType = this.DefUnitType
set node.Color = this.DefColor
set node.Name = this.DefName
set node.Sound = this.DefSound
set node.Message = this.DefMessage
set node.Time = this.DefTime
set node.TimeType = this.DefTimeType
set node.WillWait = this.DefWillWait
set i = 1
set j = stepnumber
loop
exitwhen j == 0
if this.LoadStep(j).isline then
set i = j + 1
exitwhen true
endif
set j = j - 1
endloop
call this.Lines.insertTo(node, i)
set this.actual_line = this.Lines.length
set j = i + 1
loop
exitwhen j > this.actual_line
set this.LoadLine(j).NumberLine = this.LoadLine(j).NumberLine + 1
set j = j + 1
endloop
set node.NumberLine = i
else
set node.Delay = this.DefDelay
set j = stepnumber
set i = 1
loop
exitwhen j == 0
if not this.LoadStep(j).isline then
set i = j + 1
exitwhen true
endif
set j = j - 1
endloop
call this.Actions.insertTo(node, i)
set this.actual_action = this.Actions.length
set j = i + 1
loop
exitwhen j > this.actual_action
set this.LoadAction(j).NumberAction = this.LoadAction(j).NumberAction + 1
set j = j + 1
endloop
set node.NumberAction = i
endif
call this.Steps.insertTo(node, stepnumber)
set this.actual_step = this.Steps.length
set j = stepnumber
loop
exitwhen j > this.actual_step
set this.LoadStep(j).NumberStep = this.LoadStep(j).NumberStep + 1
set j = j + 1
endloop
set node.NumberStep = stepnumber
return node
endmethod
//To set a line with all the needed values (at the beggining)
method AddLine takes unit whichUnit, playercolor whichColor, string unitName, sound soundHandle, string message, integer timeType, real timeVal, boolean wait returns TransmissionElements
local TransmissionElements what
if this.Invalid("AddLine") then
return 0
endif
set what = TransmissionElements.create_line(whichUnit, whichColor, unitName, soundHandle, message, timeType, timeVal, wait)
call this.Lines.insert(what)
set this.actual_line = this.Lines.length
set what.NumberLine = this.actual_line
call this.Steps.insert(what)
set this.actual_step = this.Steps.length
set what.NumberStep = this.actual_step
return what
endmethod
method AddLineById takes integer id, playercolor whichColor, string unitName, sound soundHandle, string message, integer timeType, real timeVal, boolean wait returns TransmissionElements
local TransmissionElements what
if this.Invalid("AddLineById") then
return 0
endif
set what = this.AddLine(null, whichColor, unitName, soundHandle, message, timeType, timeVal, wait)
set what.UnitType=id
return what
endmethod
//To add an action and (maybe) a delay to the next action or line
method AddAction takes real delay, code actions returns TransmissionElements
local TransmissionElements what
if this.Invalid("AddAction") then
return 0
endif
set what = TransmissionElements.create_action(actions, delay)
call this.Actions.insert(what)
set this.actual_action = this.Actions.length
set what.NumberAction = this.actual_action
call this.Steps.insert(what)
set this.actual_step = this.Steps.length
set what.NumberStep = this.actual_step
return what
endmethod
method AddStep takes string whattype returns TransmissionElements
local TransmissionElements what
if this.Invalid("AddStep") then
return 0
endif
set what = TransmissionElements.create(whattype)
if what == 0 then
return 0
endif
if whattype == "line" then
set what.NumberLine = this.actual_line
set what.Unit = this.DefUnit
set what.UnitType = this.DefUnitType
set what.Color = this.DefColor
set what.Name = this.DefName
set what.Sound = this.DefSound
set what.Message = this.DefMessage
set what.Time = this.DefTime
set what.TimeType = this.DefTimeType
set what.WillWait = this.DefWillWait
call this.Lines.insert(what)
set this.actual_line = this.Lines.length
set what.NumberLine = this.actual_line
else
set what.Delay = this.DefDelay
call this.Actions.insert(what)
set this.actual_action = this.Actions.length
set what.NumberAction = this.actual_action
endif
call this.Steps.insert(what)
set this.actual_step = this.Steps.length
set what.NumberStep = this.actual_step
return what
endmethod
//Main part of the system
private static method AddPlayer takes nothing returns nothing
local thistype this = thistype.instance
local player p = GetEnumPlayer()
local integer i = GetPlayerId(p)
set this.toPlayer = p
call ForceAddPlayer(this.toForce,p)
call ForceAddPlayer(this.OriginalTargetForce,p)
call AllInstances[i].insert(this)
set p = null
endmethod
static method create takes nothing returns thistype
local thistype this = thistype.allocate()
set this.allocated = true
set this.skipped = false
set this.paused = false
set this.toForce = CreateForce()
set this.OriginalTargetForce = CreateForce()
set this.t = NewTimerEx(this)
set this.actual_line = 0
set this.actual_action = 0
set this.actual_step = 0
set this.ended = false
set this.Lines = IntegerList.create()
set this.Actions = IntegerList.create()
set this.Steps = IntegerList.create()
return this
endmethod
static method CreateEx takes force toForce, integer data returns thistype
local thistype this = thistype.create()
if toForce != null and not IsForceEmpty(toForce) then
set thistype.instance = this
call ForForce(toForce, function thistype.AddPlayer)
set thistype.instance = 0
endif
set this.data = data
return this
endmethod
// You can edit the target force and the data
method SetTargetForce takes force toForce returns nothing
if this.Invalid("SetTargetForce") then
return
endif
if toForce!=null and not IsForceEmpty(toForce) then
set thistype.instance = this
call ForForce(toForce, function thistype.AddPlayer)
set thistype.instance = 0
endif
endmethod
// To add a player or force to the target force just use ForceAddPlayer(this.toForce, <your player>) or the added ForceAddForce(this.toForce,<your force>)
// If a player skips cinematic he will be removed from every instance trasnmission where is in
static method onActions takes nothing returns nothing
local player p = GetTriggerPlayer()
local integer i = GetPlayerId(p)
local integer j = 1
local thistype this
loop
exitwhen j > AllInstances[i].length
set this = AllInstances[i][j]
if this.allocated then
if IsPlayerInForce(p, this.toForce) then
call ForceRemovePlayer(this.toForce, p)
if thistype.LocalPlayer == p then
// I don't know if this is free of desync (I checked and there is not desync yet)
call EndCinematicScene()
if this.played != null then
call StopSound(this.played, false, true)
endif
endif
endif
if IsForceEmpty(this.toForce) then
call PauseTimer(this.t)
call TimerStart(this.t, RMinBJ(bj_TRANSMISSION_PORT_HANGTIME, TimerGetRemaining(this.t)), false, function thistype.callback)
endif
endif
set j = j + 1
endloop
call AllInstances[i].clear()
set p = null
endmethod
static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
loop
exitwhen i > PLAYER_NEUTRAL_AGGRESSIVE
set What_Force[i] = GetForceOfPlayer(Player(i)) //To use it if you wanna send the transmission just for 1 player
set AllInstances[i] = IntegerList.create()
call TriggerRegisterPlayerEvent(t, Player(i),EVENT_PLAYER_END_CINEMATIC)
set i = i + 1
endloop
call TriggerAddAction(t, function thistype.onActions)
set t = null
//-- --
set thistype.LocalPlayer = GetLocalPlayer()
call ForceCinematicSubtitles(true)
endmethod
endmodule
struct Transmission extends array
implement Alloc
implement M
endstruct
endlibrary
Lua:
do -- version 1.0.0
--If you wanna the system display its error message set this value to true
local DEBUG_MODE = false
--[[
Also requires the Lua Timer Utils. https://www.hiveworkshop.com/threads/lua-timerutils.316957/
How it works:
a value of type Transmission and use the function create like this:
local curr = Transmission.create()
also you can set the target force of the transmission using:
curr:SetTargetForce(toForce) This not assing a force, just move the players of the input to the actually target force, so using a function that creates a force
in the argument toForce will cause an object leak.
you can also save a value to use during the transmission using:
curr.Data = value
if you wanna add or remove a player or a force to the target force just use the normal functions ForceAddPlayer, ForceRemovePlayer and the added ForceAddForce, ForceRemoveForce
To reduce the process use:
local curr = Transmission.create(toForce, data)
Then to add a dialogue line just do:
curr:AddLine(whichUnit, color, unitName, soundHandle, message, timeType, timeVal, wait)
"whichUnit": The unit who do the dialogue line.
"color": The color of the unit in the dialogue (if you set this value to nil so then, if you have the StoreUnitColor library the unit will have their actual color, if not, it will have their owner color).
"unitName": The name of that unit.
"soundHandle": The sound that will be played during the dialogue line.
"message": What the unit will say.
"timeType": Is like the GUI function, you can use 0 (Transmission.ADD), 1 (Transmission.SET), 2 (Transmission.SUB), another value will make the wait time to 0.
"timeVal": Is like the GUI function, the normal duration of the transmission is the duration of the sound and you can edit the value depending of this and the previous value.
"wait": Is like the GUI function, if this is true the next transmission will be wait the time depending of the previous two values.
In case you don't wanna set a unit, but instead a unit-type you can use curr.AddLinebyId instead (default color BLACK)
Also you can add actions between the lines using the function:
curr:AddAction(delay, actions)
"delay": If you wanna add a delay to the next action or line.
"actions": The function that will run.
If you wanna the action run in the exact moment a line is happening just set the delay to 0.00 and put it right before the line.
If you wanna just add an extra delay just put nil in the actions part
I have to say this steps are assinged an index by the order they were declared that is the number of the step.
You can also add an action to the end of the transmission (this function will run yes or yes even you skip the transmission) using:
curr:AddEnd(func)
Where you can use the values curr=Transmission.instance to reffer to the actual instance, and with that use curr.Data if you stored one and curr.Skipped to detect
if the cinematic was skipped but if was skipped in the last line this value will remain false.
After set the lines the function
curr:Start()
To start the transmission, and to skip the transmission (the end function will run anyway) just press the ESC button,
(this will skip all the transmissions where the player is) but if the implied force has more than one player the process will continue
to the rest of players on that force (.toForce is empty). The transmission will end only if all the implied players skip the transmission (So if you use "All players"
probably you never won't skip the transmission) but you can use the function Force(player p) to have a force just for 1 player (Don't destroy those forces).
If you wanna you can also pause the transmission using:
curr:Stop(fadeout) --For the actual played sound.
or:
curr:Stop(fadeout, delay) --If you wanna the transmission resume itself after a while
And to resume manually use:
curr:Resume()
Note: The transsmision and its elements are not stored after start it, they are recycled once the transmission ends.
Other members:
toForce, to reffer the force to which it is addressed the transmission.
OriginalTargetForce, if you edited the force toForce, you can have the original force with this.
toPlayer, if the transmission is just for one player you can use this instead, in other case this will return the last added player. --(I don't know the order)
LastLineNumber, is the number of the last line what was transmitted.
NextLineNumber, is the number of the next line what will be transmitted.
ActionNumber, is the number of the actions those are running.
StepNumber, is the number of the step that is running (line or actions).
InUse, returns if the instance is allocated.
Paused, returns if the transmission is paused.
elements, to reffer to the elements instance of the actual step. Please don't edit this value
method AddSceneActions(func)
If you wanna add an action that runs everytime an step (line or action except the end) is runned, this also can be a replace of .AddAction in some
moments if you compare the number of the step with StepNumber, to know if is a line or action use the member boolean IsLine
and if is true you can use the member integer LineNumber to know the number of the actual line.
method RemoveSceneActions(func)
This remove the actions that you added before.
If you wanna set the elements by separated you can use the member
method AddStep(whattype) returns TransmissionElements
This add an step that also have an index by the order is placed, the input only can be "line" and "action"
method AddAnotherStep(stepnumber,whattype) returns TransmissionElements
This add an step but the index is set manually, if there are steps that were set after this and after the transmission was started
those index will be moved.
Just if you wanna remove one
method RemoveStep(stepnumber,reorder)
This remove an step using the number of the step, in this case the reasing of the next steps is optional because here is no risk of overwrite.
After add the step the returned value have their own members
Unit This is the unit who will be marked during the transmission (not who will apear in the transmission).
UnitType
Color
Name
Sound
Message
Time
TimeType
WillWait
Delay
NumberLine
NumberAction
NumberStep
method SetActions(func) This replace all the actions
method AddMoreAction(func) This add action without affecting the added actions
method GetActions() returns a Array<functions>
But if you wanna use this members using the number of the step use their "ByIndex" methods (these are members of Transmission not of TransmissionElements)
You have to do:
curr:$Name of the member$ByIndex(stepnumber,$type of the member$)
$type of the member$=curr:$Name of the member$ByIndex(stepnumber)
Example:
local curr = Transmission.create()
curr:AddStep("line") --is the step 1
curr:AddStep("action") --is the step 2
curr:SetUnitByIndex(1, <your unit>)
curr:SetActionsByIndex(2, <your actions>)
A replace of:
local curr = Transmission.create()
local what
what = curr:AddStep("line")
what:SetUnit(<your unit>)
what = curr:AddStep("action")
what:SetActions(<your actions>)
If you wanna get the TransmissionElements instance using that index just do:
<variable> = curr:LoadElement(stepnumber)
But if you wanna save the number of lines you can set default values (before than create the steps)
DefUnit
DefUnitType
DefColor
DefName
DefSound
DefMessage
DefTime
DefTimeType
DefWillWait
DefDelay
]]--
--A function to use
local Temp2 = 0
local What_Force = {}
local Other = nil
function IsForceEmpty(f)
if f then
local Temp = 0
ForForce(f, function ()
Temp = Temp + 1
end)
return Temp == 0
end
return true
end
function ForceAddForce(principal, other)
if not principal or not other then
return
else
ForForce(principal ,function ()
ForceAddPlayer(other,GetEnumPlayer())
end)
end
end
function ForceRemoveForce(principal, other)
if not principal or not other then
return
else
ForForce(principal ,function ()
ForceRemovePlayer(other,GetEnumPlayer())
end)
end
end
function Force(p)
return What_Force[GetPlayerId(p)]
end
function DebugError(msg)
if DEBUG_MODE then
error(msg, 3)
else
error("")
end
end
-- This are the elements of each line transmission
TransmissionElementsMT = {
__index = function (t, k)
-- Define only what fields can have your object
if TransmissionElementsMT[k] then
return TransmissionElementsMT[k]
elseif k == "isline" or
k == "NumberStep" then
return rawget(t, k)
elseif k == "Unit" or
k == "UnitType" or
k == "Color" or
k == "Name" or
k == "Sound" or
k == "Message" or
k == "TimeType" or
k == "Time" or
k == "WillWait" or
k == "NumberLine" then
if not rawget(t, "isline") then
DebugError("This is an action")
end
return rawget(t, k)
elseif k == "Delay" or
k == "actions" or
k == "NumberAction" then
if rawget(t, "isline") then
DebugError("This is a line")
end
return rawget(t, k)
else
DebugError("This field doesn't exist " .. tostring(k))
end
end,
__newindex = function (t, k , v)
-- Define only what fields can have your object
if TransmissionElementsMT[k] then
DebugError("You can't edit this method")
elseif k == "isline" or
k == "NumberStep" then
rawset(t, k, v)
elseif k == "Unit" or
k == "UnitType" or
k == "Color" or
k == "Name" or
k == "Sound" or
k == "Message" or
k == "TimeType" or
k == "Time" or
k == "WillWait" or
k == "NumberLine" then
if not rawget(t, "isline") then
DebugError("This is an action")
end
rawset(t, k, v)
elseif k == "Delay" or
k == "actions" or
k == "NumberAction" then
if rawget(t, "isline") then
DebugError("This is a line")
end
rawset(t, k, v)
else
DebugError("You can't add new fields " .. tostring(k))
end
end,
__name = "TransmissionElements",
__tostring = function (t)
local result = ""
if t.isline then
result = result .. "Unit: " .. tostring(t.Unit) .. "\n"
result = result .. "UnitType: " .. tostring(t.UnitType) .. "\n"
result = result .. "Color: " .. tostring(t.Color) .. "\n"
result = result .. "Name: " .. tostring(t.Name) .. "\n"
result = result .. "Sound: " .. tostring(t.Message) .. "\n"
result = result .. "TimeType: " .. tostring(t.TimeType) .. "\n"
result = result .. "Time: " .. tostring(t.Time) .. "\n"
result = result .. "WillWait: " .. tostring(t.WillWait) .. "\n"
else
result = result .. "Delay: " .. tostring(t.Delay) .. "\n"
for k, v in ipairs(t.actions) do
result = result .. "Action ".. k .. ": " .. tostring(v) .. "\n"
end
end
return result
end
}
function TransmissionElementsMT:SetActions(func)
if self.isline then
DebugError("This is a line |cffff0000SetActions|r")
end
self.actions = {}
if func then
table.insert(self.actions, func)
end
end
function TransmissionElementsMT:AddMoreActions(func)
if self.isline then
DebugError("This is a line |cffff0000AddMoreActions|r")
-- Only add actions if func is not nil and self.actions was created previously
elseif not func then
return
elseif not self.actions then
DebugError("Why do you wanna add \"more\" actions if there isn't even a trigger with actions?")
else
table.insert(self.actions, func)
end
end
function TransmissionElementsMT:GetActions()
if self.isline then
DebugError("This is a line |cffff0000GetActions|r")
end
return self.actions
end
TransmissionElements = setmetatable({}, {
__index = function (t, k)
if k == "create" or
k == "create_action" or
k == "create_line" then
rawget(t, k)
else
DebugError("This field don't exist " .. tostring(k))
end
end,
__newindex = function (t, k, v)
-- Define only what fields can have your class
if k == "create" or
k == "create_action" or
k == "create_line" then
rawset(t, k , v)
else
DebugError("You can't add new fields " .. tostring(k))
end
end,
__name = "TransmissionElements",
__tostring = function (t)
return "Class: TransmissionElements"
end
})
function TransmissionElements.create(whattype)
local self = setmetatable({}, TransmissionElementsMT)
if whattype == "line" then
self.isline = true
elseif whattype == "action" then
self.isline = false
else
DebugError("Invalid type.")
end
return self
end
function TransmissionElements.create_action(delay, func)
local self = TransmissionElements.create("action")
-- Now I can store functions :D
if not self.actions then
self.actions = {}
end
if func then
table.insert(self.actions, func)
end
self.Delay = delay
return self
end
function TransmissionElements.create_line(whichUnit, whichColor, unitName, soundHandle, message, timeType, timeVal, wait)
local self = TransmissionElements.create("line")
self.Unit = whichUnit
self.Name = unitName
self.Sound = soundHandle
self.Message = message
self.TimeType = timeType
self.Time = timeVal
self.WillWait = wait
if not whichUnit then
self.UnitType = 0
if not whichColor then
if LIBRARY_StoreUnitColor then
self.Color = PLAYER_COLOR_BLACK
else
self.Color = ConvertPlayerColor(PLAYER_NEUTRAL_AGGRESSIVE)
end
else
self.Color = whichColor
end
else
self.UnitType = GetUnitTypeId(whichUnit)
if not whichColor then
if LIBRARY_StoreUnitColor then
self.Color = GetUnitColor(whichUnit)
else
self.Color = GetPlayerColor(GetOwningPlayer(whichUnit))
end
else
self.Color = whichColor
end
end
return self
end
--*****************************************************
--********The actual struct of the transmission********
--*****************************************************
local LocalPlayer = nil
local AllInstances = {}
for i = 0, PLAYER_NEUTRAL_AGGRESSIVE do
AllInstances[Player(i)] = {}
end
TransmissionMT = {
__index = function (t, k)
-- Define only what fields can have your object
if TransmissionMT[k] then
return TransmissionMT[k]
elseif k == "toPlayer" or
k == "toForce" or
k == "OriginalTargetForce" or
k == "elements" or
k == "DefUnit" or
k == "DefUnitType" or
k == "DefColor" or
k == "DefName" or
k == "DefSound" or
k == "DefMessage" or
k == "DefTime" or
k == "DefTimeType" or
k == "DefWillWait" or
k == "DefDelay" or
k == "Data" or
k == "Paused" or
k == "Skipped" or
k == "played" or
k == "final" or
k == "t" or
k == "actual_line" or
k == "actual_action" or
k == "actual_step" or
k == "StepNumber" or
k == "ActionNumber" or
k == "LineNumber" or
k == "ended" or
k == "scene" or
k == "Steps" or
k == "Lines" or
k == "Actions" then
return rawget(t, k)
else
DebugError("This field doesn't exist " .. tostring(k))
end
end,
__newindex = function (t, k , v)
-- Define only what fields can have your object
if TransmissionMT[k] then
DebugError("You can't edit this method")
elseif k == "toPlayer" or
k == "toForce" or
k == "OriginalTargetForce" or
k == "elements" or
k == "DefUnit" or
k == "DefUnitType" or
k == "DefColor" or
k == "DefName" or
k == "DefSound" or
k == "DefMessage" or
k == "DefTime" or
k == "DefTimeType" or
k == "DefWillWait" or
k == "DefDelay" or
k == "Data" or
k == "Paused" or
k == "Skipped" or
k == "played" or
k == "final" or
k == "t" or
k == "actual_line" or
k == "actual_action" or
k == "actual_step" or
k == "StepNumber" or
k == "ActionNumber" or
k == "LineNumber" or
k == "ended" or
k == "scene" or
k == "Steps" or
k == "Lines" or
k == "Actions" then
rawset(t, k, v)
else
DebugError("You can't add new fields " .. tostring(k))
end
end,
__len = function (t)
return #t.Steps
end,
__name = "Transmission",
__tostring = function (t)
local result = ""
for _, v in ipairs(t.Steps) do
result = result .. tostring(v)
end
return result
end
}
-- When the transmission ends
function TransmissionMT:finish()
ReleaseTimer(self.t)
self.ended = true
if self.final then
Transmission.instance = self
for _, func in ipairs(self.final) do
func()
end
Transmission.instance = nil
self.final = nil
end
ForForce(self.toForce,function ()
for k, v in ipairs(AllInstances[GetEnumPlayer()]) do
if v == self then
table.remove(AllInstances[GetEnumPlayer()], k)
break
end
end
end)
DestroyForce(self.OriginalTargetForce)
DestroyForce(self.toForce)
self.Steps = nil
self.Lines = nil
self.Actions = nil
self.scene = nil
self.elements = nil
self.scene = nil
self.Data = nil
self.played = nil
self.toForce = nil
self.toPlayer = nil
self.t = nil
end
function TransmissionMT:what_call()
self.StepNumber = self.StepNumber + 1
self.elements = self.Steps[self.StepNumber]
if not self.elements or IsForceEmpty(self.toForce) then
-- If the cinematic was skipped just in the last line it won't be counted as skipped
self.Skipped = IsForceEmpty(self.toForce) and self.elements
self:finish()
else
if self.elements.isline then
self.LineNumber = self.LineNumber + 1
self:cinematic_line()
else
self.ActionNumber = self.ActionNumber + 1
self:cinematic_actions()
end
end
end
local function callback()
GetTimerData():what_call()
end
-- Where the magic happens
function TransmissionMT:cinematic_line()
local alpha = 0
local delay = 0.00
local what = self.elements
self.played = what.Sound
bj_lastTransmissionDuration = GetTransmissionDuration(self.played, what.TimeType, what.Time)
if IsPlayerInForce(LocalPlayer, self.toForce) then
if self.played then
StartSound(self.played)
end
SetCinematicScene(what.UnitType, what.Color, what.Name, what.Message, bj_lastTransmissionDuration + bj_TRANSMISSION_PORT_HANGTIME, bj_lastTransmissionDuration)
alpha = 255
end
if what.Unit then
UnitAddIndicator(what.Unit, 255, 255, 255, alpha)
end
if self.scene then
Transmission.instance = self
for _, func in ipairs(self.scene) do
func()
end
Transmission.instance = nil
end
if what.WillWait then
delay = bj_lastTransmissionDuration
end
if delay > 0.00 then
TimerStart(self.t, delay, false, callback)
else
self:what_call()
end
end
function TransmissionMT:cinematic_actions()
local what = self.elements
Transmission.instance = self
pcall(function ()
for _, func in ipairs(what:GetActions()) do
func()
end
end)
if self.scene then
for _, func in ipairs(self.scene) do
func()
end
end
Transmission.instance = nil
if what.Delay > 0.00 then
TimerStart(self.t, what.Delay, false, callback)
else
self:what_call()
end
end
function TransmissionMT:Resume()
if self.Paused then
self.Paused=false
ResumeTimer(self.t)
if IsPlayerInForce(LocalPlayer, self.toForce) then
StartSound(this.played)
end
SetSoundOffsetBJ(TimerGetElapsed(self.t), self.played) -- In case of desync
end
end
function TransmissionMT:Stop(fadeout, delay)
self.Paused = true
PauseTimer(self.t)
StopSound(self.played, false, fadeout)
if delay then
if delay>0.00 then
TimerStart(NewTimer(), delay, false, function ()
self:Resume()
ReleaseTimer()
end)
else
self:Resume()
warn("Why do you even use this method?, Ay.")
end
end
end
function TransmissionMT:Start()
-- If there is not a line or action so it just go to the end
self.LineNumber = 0
self.ActionNumber = 0
self.StepNumber = 0
self.elements = nil
self:what_call()
end
-- Now I can save functions :D
function TransmissionMT:AddEnd(func)
if not self.final then
self.final = {}
end
table.insert(self.final, func)
end
-- To add an action everytime a line or action is runned
function TransmissionMT:AddSceneActions(func)
if not self.scene then
self.scene = {}
end
table.insert(self.scene, func)
return #self.scene
end
-- But if you wanna remove it
function TransmissionMT:RemoveSceneActions(actions)
table.remove(self.scene, actions)
end
--To set and get the values using their step position
function TransmissionMT:SetUnitByIndex(stepnumber, value)
self.Steps[stepnumber].Unit = value
end
function TransmissionMT:GetUnitByIndex(stepnumber)
return self.Steps[stepnumber].Unit
end
function TransmissionMT:SetUnitTypeByIndex(stepnumber, value)
self.Steps[stepnumber].UnitType = value
end
function TransmissionMT:GetUnitTypeByIndex(stepnumber)
return self.Steps[stepnumber].UnitType
end
function TransmissionMT:SetColorByIndex(stepnumber, value)
self.Steps[stepnumber].Color = value
end
function TransmissionMT:GetColorByIndex(stepnumber)
return self.Steps[stepnumber].Color
end
function TransmissionMT:SetNameByIndex(stepnumber, value)
self.Steps[stepnumber].Name = value
end
function TransmissionMT:GetNameByIndex(stepnumber)
return self.Steps[stepnumber].Name
end
function TransmissionMT:SetSoundByIndex(stepnumber, value)
self.Steps[stepnumber].Sound = value
end
function TransmissionMT:GetSoundByIndex(stepnumber)
return self.Steps[stepnumber].Sound
end
function TransmissionMT:SetMessageByIndex(stepnumber, value)
self.Steps[stepnumber].Message = value
end
function TransmissionMT:GetMessageByIndex(stepnumber)
return self.Steps[stepnumber].Message
end
function TransmissionMT:SetTimeTypeByIndex(stepnumber, value)
self.Steps[stepnumber].TimeType = value
end
function TransmissionMT:GetTimeTypeByIndex(stepnumber)
return self.Steps[stepnumber].TimeType
end
function TransmissionMT:SetTimeByIndex(stepnumber, value)
self.Steps[stepnumber].Time = value
end
function TransmissionMT:GetTimeByIndex(stepnumber)
return self.Steps[stepnumber].Time
end
function TransmissionMT:SetWillWaitByIndex(stepnumber, value)
self.Steps[stepnumber].WillWait = value
end
function TransmissionMT:WillWaitByIndex(stepnumber)
return self.Steps[stepnumber].WillWait
end
function TransmissionMT:SetDelayByIndex(stepnumber, value)
self.Steps[stepnumber].Delay = value
end
function TransmissionMT:GetDelayByIndex(stepnumber)
return self.Steps[stepnumber].Delay
end
function TransmissionMT:SetActionsByIndex(stepnumber, func)
self.Steps[stepnumber].SetActions(func)
end
function TransmissionMT:GetActionsByIndex(stepnumber)
self.Steps[stepnumber].GetActions()
end
function TransmissionMT:AddMoreActionsByIndex(stepnumber, func)
self.Steps[stepnumber].AddMoreActions(func)
end
--If you wanna remove a step
function TransmissionMT:RemoveStep(stepnumber)
local what = table.remove(self.Steps, stepnumber)
if what.isline then
for k, v in ipairs(self.Lines) do
if what == v then
table.remove(self.Lines, k)
break
end
end
self.actual_line = #self.Lines
for j = stepnumber, self.actual_line do
self.Lines[j].NumberLine = self.Steps[j].NumberLine - 1
end
else
for k, v in ipairs(self.Actions) do
if what == v then
table.remove(self.Actions, k)
break
end
end
self.actual_action = #self.Actions
for j = stepnumber, self.actual_action do
self.Actions[j].NumberAction = self.Steps[j].NumberAction - 1
end
end
self.actual_step = #self.Steps
for j = stepnumber, self.actual_step do
self.Steps[j].NumberStep = self.Steps[j].NumberStep - 1
end
return what
end
-- If you wanna add another step
function TransmissionMT:AddAnotherStep(stepnumber, whattype)
local node = TransmissionElements.create(whattype)
-- Insert to list
table.insert(self.Steps, stepnumber, node)
node.NumberStep = stepnumber
local i = 0 -- To look in what part I should insert the line or action
if whattype == "line" then
node.Unit = self.DefUnit
node.UnitType = self.DefUnitType or 0
node.Color = self.DefColor
node.Name = self.DefName or ""
node.Sound = self.DefSound
node.Message = self.DefMessage or ""
node.Time = self.DefTime or 0.00
node.TimeType = self.DefTimeType or 1
node.WillWait = self.DefWillWait
for j = stepnumber, 0, -1 do
if self.Steps[j].isline then
i = j + 1
break
end
end
table.insert(self.Lines, i, node)
self.actual_line = #self.Lines
self.NumberLine = i
else
node.Delay = self.DefDelay or 0.00
for j = stepnumber, 0, -1 do
if not self.Steps[j].isline then
i = j + 1
break
end
end
table.insert(self.Actions, i, node)
self.actual_action = #self.Actions
self.NumberAction = i
end
return node
end
-- To set a line with all the needed values (at the beggining)
function TransmissionMT:AddLine(whichUnit, whichColor, unitName, soundHandle, message, timeType, timeVal, wait)
local what = TransmissionElements.create_line(whichUnit, whichColor, unitName, soundHandle, message, timeType, timeVal, wait)
table.insert(self.Lines, what)
self.actual_line = #self.Lines
what.NumberLine = self.actual_line
table.insert(self.Steps, what)
self.actual_step = #self.Steps
what.NumberStep = self.actual_step
return what
end
function TransmissionMT:AddLineById(id, whichColor, unitName, soundHandle, message, timeType, timeVal, wait)
local what = self:AddLine(nil, whichColor, unitName, soundHandle, message, timeType, timeVal, wait)
what.UnitType = id
return what
end
-- To add an action and (maybe) a delay to the next action or line
function TransmissionMT:AddAction(delay, actions)
local what = TransmissionElements.create_action(delay, actions)
table.insert(self.Actions, what)
self.actual_action = #self.Actions
table.insert(self.Steps, what)
self.actual_step = #self.Steps
what.NumberStep = self.actual_step
return what
end
function TransmissionMT:AddStep(whattype)
local what = TransmissionElements.create(whattype)
if whattype == "line" then
table.insert(self.Lines, what)
self.actual_line = #self.Lines
what.NumberLine = self.actual_line
what.Unit = self.DefUnit
what.UnitType = self.DefUnitType or 0
what.Color = self.DefColor
what.Name = self.DefName or ""
what.Sound = self.DefSound
what.Message = self.DefMessage or ""
what.Time = self.DefTime or 0.00
what.TimeType = self.DefTimeType or 1
what.WillWait = self.DefWillWait
else
table.insert(self.Actions, what)
self.actual_action = #self.Actions
what.Delay = self.DefDelay or 0.00
end
table.insert(self.Steps, what)
self.actual_step = #self.Steps
what.NumberStep = self.actual_step
return what
end
-- You can edit the target force and the data
function TransmissionMT:SetTargetForce(toForce)
if toForce and not IsForceEmpty(toForce) then
ForForce(toForce, function ()
local p = GetEnumPlayer()
self.toPlayer = p
ForceAddPlayer(self.toForce, p)
ForceAddPlayer(self.OriginalTargetForce, p)
table.insert(AllInstances[p], self)
end)
end
end
Transmission = setmetatable({
ADD = bj_TIMETYPE_ADD, -- 0
SET = bj_TIMETYPE_SET, -- 1
SUB = bj_TIMETYPE_SUB, -- 2
}, {
__index = function (t, k)
if k == "create" or
k == "ADD" or
k == "SET" or
k == "SUB" or
k == "instance" then
rawget(t, k)
else
DebugError("This field doesn't exist " .. tostring(k))
end
end,
__newindex = function (t, k, v)
-- Define only what fields can have your class
if k == "ADD" or
k == "SET" or
k == "SUB" then
DebugError("This value is a constant " .. tostring(k))
elseif k == "create" or
k == "instance" then
rawset(t, k , v)
else
DebugError("You can't add new fields " .. tostring(k))
end
end,
__name = "Transmission",
__tostring = function (t)
return "Class: Transmission"
end
})
function Transmission.create(toForce, data)
if toForce and not data then
DebugError("Insufficient arguments")
end
local self = setmetatable({}, TransmissionMT)
self.Skipped = false
self.Paused = false
self.toForce = CreateForce()
self.OriginalTargetForce = CreateForce()
self.t = NewTimer(self)
self.actual_line = 0
self.actual_action = 0
self.actual_step = 0
self.ended = false
self.Steps = {}
self.Lines = {}
self.Actions = {}
if toForce and data then
if not IsForceEmpty(toForce) then
ForForce(toForce, function ()
local p = GetEnumPlayer()
self.toPlayer = p
ForceAddPlayer(self.toForce, p)
ForceAddPlayer(self.OriginalTargetForce, p)
table.insert(AllInstances[p], self)
end)
end
self.Data = data
end
return self
end
-- To add a player or force to the target force just use ForceAddPlayer(this.toForce, <your player>) or the added ForceAddForce(this.toForce, <your force>)
local oldinit = InitBlizzard
function InitBlizzard()
local t = CreateTrigger()
for i = 0, PLAYER_NEUTRAL_AGGRESSIVE do
What_Force[i] = GetForceOfPlayer(Player(i)) -- To use it if you wanna send the transmission just for 1 player
TriggerRegisterPlayerEvent(t, Player(i), EVENT_PLAYER_END_CINEMATIC)
end
-- If a player skips cinematic he will be removed from every instance trasnmission where is in
TriggerAddAction(t, function ()
local p = GetTriggerPlayer()
for _, self in ipairs(AllInstances[p]) do
if IsPlayerInForce(p, self.toForce) then
ForceRemovePlayer(self.toForce, p)
if p == LocalPlayer then
-- I don't know if this is free of desync (I checked and there is not desync yet)
EndCinematicScene()
if self.played then
StopSound(self.played, false, true)
end
end
end
if IsForceEmpty(self.toForce) then
PauseTimer(self.t)
TimerStart(self.t, RMinBJ(bj_TRANSMISSION_PORT_HANGTIME, TimerGetRemaining(self.t)), false, callback)
end
end
AllInstances[p] = {}
end)
-- --
LocalPlayer = GetLocalPlayer()
ForceCinematicSubtitles(true)
-- --
oldinit()
end
end
v1.0
- Released
- Added AddStep method with individual methods to edit the values.
- Added Stop and Resume functions to the transmissions.
- See the comment section to more information.
- Fixed some problems and member OriginalTargetForce added.
- Fixed some problems I didn't notice because I'm too distracted.
- Fixed AddAnotherStep, and now RemoveStep always reorder (I'm too lazy to recycle them).
- More polished.
- Some methods changed its names for better ones (see the API) and the parameters of AddActions were interchanged.
- Don't need Table or even use a hashtable, but yes or yes needs a library that requires Table XD (ArrayList).
v1.0
- Released