khanat-client-data-NeL/data/ryz/ryz_ring/r2_features_npc_groups.lua

592 lines
22 KiB
Lua

local registerFeature = function ()
local feature={}
feature.Name="NpcGrpFeature"
feature.Description="The default feature"
local NpcGrpFeatureVersion = 1
feature.Components=
{
NpcGrpFeature = {
BaseClass="ActiveLogicEntity",
Name="NpcGrpFeature",
Version = NpcGrpFeatureVersion,
InEventUI = true,
--Menu="ui:interface:r2ed_base_menu",
--Menu="ui:interface:r2ed_entity_menu",
DisplayerUI = "R2::CDisplayerLua",
DisplayerUIParams = "groupUIDisplayer",
DisplayerVisual = "R2::CDisplayerVisualGroup",
DisplayerProperties = "R2::CDisplayerLua",
DisplayerPropertiesParams = "npcGroupPropertySheetDisplayer",
DisplayerVisualParams =
{
Look = r2.PrimRender.GroupLook,
ArrayName = "Components"
},
-----------------------------------------------------------------------------------------------
Parameters = {
},
ApplicableActions = {
"Activate",
"Deactivate", "Kill", "begin activity sequence",
"Sit Down", "Stand Up",
"Fight with player", "Fight with Npcs",
"Dont fight with player", "Dont fight with Npcs",
"Run", "Dont run",
--"begin chat sequence"
},
Events = {
"activation",
"desactivation", "member death", "group death",
"end of activity step", "end of activity sequence",
"begin of activity step", "begin of activity sequence",
"targeted by player",
--"end of chat step", "end of chat sequence"
},
Conditions = {
--"is active", "is inactive",
"is in activity sequence",
"is in activity step", --"is in chat sequence", "is in chat step"
"is dead", "is alive",
},
TextContexts = {
"a member is dead", "a member is dead", "a member is alive",
"group is dead", "group is alive"
},
TextParameters = {
"members number"
},
LiveParameters = {
"is active", "current activity sequence and activity step",
"current chat sequence and chat step"
},
-----------------------------------------------------------------------------------------------
Prop =
{
{Name="Name", Type="String", MaxNumChar="32"},
{Name="Components", Type="Table"},
},
TreeIcon= function(this)
if this.Components.Size>0 and (this.Components[0]:isKindOf("NpcCreature") or this.Components[0]:isKindOf("NpcPlant")) then
return "r2ed_icon_group_creatures.tga"
else
return "r2ed_icon_group.tga"
end
return ""
end,
PermanentTreeIcon= function(this)
if this.Components.Size>0 and (this.Components[0]:isKindOf("NpcCreature") or this.Components[0]:isKindOf("NpcPlant")) then
return "r2ed_icon_permanent_group_creatures.tga"
else
return "r2ed_icon_permanent_group.tga"
end
return ""
end,
---------------------------------------------------------------------------------------------------------
-- get select bar type
SelectBarType = function(this)
return i18n.get("uiR2EDScene"):toUtf8()
end,
updateVersion = function(this, scenarioValue, currentValue )
local patchValue = scenarioValue
-- version 1 : Remove the "Cost" field -> hold locally now
if patchValue < 1 then
r2.requestEraseNode(this.InstanceId, "Cost", -1)
patchValue = 1
end
if patchValue == currentValue then return true end
return false
end,
-----------------------------------------------------------------------------------------------
-- from base class
isCopyable = function(this)
return true
end,
--------------------------------------------------------------------------------------------
-- from WorldObject
canChangeDisplayMode = function(this)
return true
end,
-- from WorldObject
isDisplayModeToggleSupported = function(this, displayMode)
return this.Components[0]:isDisplayModeToggleSupported(displayMode)
end,
getAvailableCommands = function(this, dest)
r2.Classes.ActiveLogicEntity.getAvailableCommands(this, dest)
this:getAvailableDisplayModeCommands(dest)
end,
-----------------------------------------------------------------------------------------------
-- from base class
-- additionnal parameter 'srcOptions' gives the options inherited
paste = function(src, newPlace, srcInstanceId, srcOptions)
local options
if not srcOptions then
options =
{
CopyEvents = 0,
CopyActivities = 0,
-- CopyChatSequences = 0
DuplicateGroup = -1 -- option available when duplicating leader only
}
end
local function paramsOk(options)
if not r2:checkAiQuota(table.getn(src.Components)) then return end
if options.CopyActivities == 0 then
src.ActivitiesId = {}
src.Components[1].Behavior.Activities = {}
end
if options.CopyEvents == 0 then
src.Components[1].Behavior.Actions = {}
end
--if options.CopyChatSequences == 0 then
-- src.Behavior.ChatSequences = {}
-- end
if newPlace then
-- compute min position and use as group ref pos
local mx = 0
local my = 0
local mz = 0
-- compute center
for k, v in pairs(src.Components) do
v.Position.x = v.Position.x + src.Position.x
v.Position.y = v.Position.y + src.Position.y
v.Position.z = v.Position.z + src.Position.z
mx = mx + v.Position.x
my = my + v.Position.y
mz = mz + v.Position.z
end
mx = mx / table.getn(src.Components)
my = my / table.getn(src.Components)
mz = mz / table.getn(src.Components)
-- make relative to center
for k, v in pairs(src.Components) do
v.Position.x = v.Position.x - mx
v.Position.y = v.Position.y - my
v.Position.z = v.Position.z - mz
end
-- compute new center
if type(newPlace) == "table" then
src.Position.x, src.Position.y, src.Position.z = newPlace.x, newPlace.y, newPlace.z
else
src.Position.x, src.Position.y, src.Position.z = r2:getPastePosition()
end
end
r2:setCookie(src.InstanceId, "Select", true)
-- insert in current act
r2.requestInsertNode(r2:getCurrentAct().InstanceId, "Features", -1,"", src)
end
if srcOptions then
-- if options were given, do not ask the user
paramsOk(srcOptions)
return
end
local function paramsCancel()
debugInfo('paste was cancelled')
end
if table.getn(src.Components[1].Behavior.Activities) == 0 then
options.CopyActivities = -1
end
if table.getn(src.Components[1].Behavior.Actions) == 0 then
options.CopyEvents = -1
end
--if table.getn(src.Behavior.ChatSequences) == 0 then
-- options.CopyChatSequences = -1
-- end
if options.CopyActivities >= 0 or
options.CopyEvents >= 0
--or options.CopyChatSequences >= 0
then
r2:doForm("SpecialPaste", options, paramsOk, paramsCancel)
else
-- nothing specific to copy, do direct paste
paramsOk(options)
end
end,
-----------------------------------------------------------------------------------------------
-- from base class
pasteGhost = function(src)
if not r2:checkAiQuota(table.getn(src.Components)) then return end
local target = r2:getCurrentAct():getDefaultFeature()
-- create the 'Ghosts' entry locally if it doesn't already exists
if target.Ghosts == nil then
r2.requestInsertGhostNode(target.InstanceId, "", -1, "Ghosts", {})
end
--
r2.requestInsertGhostNode(target.InstanceId, "Ghosts",-1,"", src)
-- insertion should have been done right now
return r2:getInstanceFromId(src.InstanceId)
end,
---------------------------------------------------------------------------------------------------------
-- create a new copy with renaming
newCopy = function(this)
local result = r2.Classes.BaseClass.newCopy(this)
local counterNames = {}
for k, v in pairs(result.Components) do
local category = r2.getPropertyValue(v, "Category")
local subCategory = r2.getPropertyValue(v, "SubCategory")
if category == "Npc" then
if subCategory=="Kami" or subCategory=="Karavan" then
local baseName = r2.PaletteIdToTranslation[this.Components[k-1].Base]
if counterNames[baseName]==nil then
local uc_name = ucstring()
uc_name:fromUtf8(baseName)
local name = r2:genInstanceName(uc_name):toUtf8()
counterName = string.gsub(name, tostring(uc_name), "")
counterNames[baseName] = tonumber(counterName)
else
counterNames[baseName] = counterNames[baseName]+1
end
v.Name = baseName .. " " .. counterNames[baseName]
else
local sex
local sheetClient = r2.getPropertyValue(v, "SheetClient")
if isR2PlayerMale(sheetClient) then
sex = r2.male
else
sex = r2.female
end
local race = getR2PlayerRace(sheetClient)
v.Name = r2:randomNPCName2(race, sex)
end
end
end
return result
end,
---------------------------------------------------------------------------------------------------------
-- From logic entity
getCategory = function(this)
return this.Components[0]:getCategory()
end,
---------------------------------------------------------------------------------------------------------
-- From logic entity
getSubCategory = function(this)
return this.Components[0]:getSubCategory()
end,
-----------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------
-- cut / paste
accept = function(this, targetInstance)
return r2:testCanGroup(targetInstance, this)
end,
--
insert = function(this, instanceToInsert)
assert(r2:testCanGroup(instanceToInsert, this))
r2:group(instanceToInsert, this)
end,
---------------------------------------------------------------------------------------------------------
getFirstSelectableSon = function(this)
for k = 0, this.Components.Size - 1 do
if this.Components[k].Selectable then
return this.Components[k]
end
end
return nil
end,
---------------------------------------------------------------------------------------------------------
isNextSelectable = function(this)
return true
end,
-----------------------------------------------------------------------------------------------
-- from base class
getParentTreeNode = function(this)
-- if not this.ParentInstance:isKindOf("Act") then
-- return r2.Classes.BaseClass.getParentTreeNode(this)
-- end
-- return this:getParentAct():getContentTreeNodes("people")
if this.Components.Size>0 and (this.Components[0]:isKindOf("NpcCreature") or this.Components[0]:isKindOf("NpcPlant")) then
return this:getParentAct():getContentTreeNodes("creatures")
else
return this:getParentAct():getContentTreeNodes("people")
end
end,
---------------------------------------------------------------------------------------------------------
-- from base class
appendInstancesByType = function(this, destTable, kind)
assert(type(kind) == "string")
--this:delegate():appendInstancesByType(destTable, kind)
r2.Classes.BaseClass.appendInstancesByType(this, destTable, kind)
for k, component in specPairs(this.Components) do
component:appendInstancesByType(destTable, kind)
end
end,
---------------------------------------------------------------------------------------------------------
-- from base class
hasScenarioCost = function(this)
return true
end,
pretranslate = function (this, context)
debugInfo("##pretranslate npcgrp##")
local feature = this
local components = this.Components
-- create one RtNpcGrp by group
local rtNpcGrp = r2.newComponent("RtNpcGrp")
table.insert(context.RtAct.NpcGrps, rtNpcGrp)
-- register the groupe
context.RtGroups[feature.InstanceId] = rtNpcGrp
context.RtGroups[feature.InstanceId].Name = rtNpcGrp.Id
-- register all group components
local key, comp = next(components, nil)
while (key ~= nil) do
context.RtGroups[comp.InstanceId] = rtNpcGrp
context.RtGroups[comp.InstanceId].Name = rtNpcGrp.Id
key, comp = next(components, key)
end
end,
pretranslate2 = function(this, context)
--
context.Feature = this
--
local scenario = context.Scenario
local components = context.Feature.Components
local leader = components[0]
local hlComponent = context.Feature
assert(components.Size >= 1)
local rtNpcGrp = r2.Translator.getRtGroup(context, context.Feature.InstanceId)
-- translate actionHandlers
local aiActivity = r2.Translator.getAiActivity(leader)
r2.Translator.translateActivities(context, hlComponent, hlComponent:getBehavior(), rtNpcGrp, aiActivity)
end,
translate = function (this, context)
--
context.Feature = this
--
local scenario = context.Scenario
local components = context.Feature.Components
local leader = components[0]
local hlComponent = context.Feature
assert(components.Size >= 1)
local rtNpcGrp = r2.Translator.getRtGroup(context, context.Feature.InstanceId)
--if there's no sequence for the group,
--create a state with no movement, and put the group in it.
local key, comp = next(components, nil)
while key do
if (comp.isKindOf and comp:isKindOf( "Npc") ) then
context.Component = comp
-- insert Npc
local rtNpc = r2.Translator.translateNpc(comp, context)
table.insert(context.RtAct.Npcs, rtNpc)
table.insert(rtNpcGrp.Children, rtNpc.Id)
end
key, comp = next(components, key)
end
-- dump every action of the ai
-- r2.dumpAi(rpcGrp)
r2.Translator.setGroupParameters (leader, rtNpcGrp)
-- translate actionHandlers
-- local aiActivity = r2.Translator.getAiActivity(leader)
-- r2.Translator.translateActivities(context, hlComponent, hlComponent:getBehavior(), rtNpcGrp, aiActivity)
-- set eventHandlers
r2.Translator.translateEventHandlers(context, hlComponent, hlComponent:getBehavior().Actions, rtNpcGrp)
--> events = leader or npc
end
}
}
-- same for group and for npc
local component = feature.Components.NpcGrpFeature
component.getLogicCondition = r2.Translator.getNpcLogicCondition
component.getLogicAction = r2.Translator.getNpcLogicAction
component.getLogicEvent = r2.Translator.getNpcLogicEvent
----------------------------------------------------------------------------
-- add a line to the event menu
function component:getLogicTranslations()
local logicTranslations = {
["ApplicableActions"] = {
["Activate"] = { menu=i18n.get( "uiR2AA0Spawn" ):toUtf8(),
text=i18n.get( "uiR2AA1Spawn" ):toUtf8()},
["Deactivate"] = { menu=i18n.get( "uiR2AA0Despawn" ):toUtf8(),
text=i18n.get( "uiR2AA1Despawn" ):toUtf8()},
["Sit Down"] = { menu=i18n.get( "uiR2AA0NpcSit" ):toUtf8(),
text=i18n.get( "uiR2AA1NpcSit" ):toUtf8(),
groupIndependant=true},
["Stand Up"] = { menu=i18n.get( "uiR2AA0NpcStand" ):toUtf8(),
text=i18n.get( "uiR2AA1NpcStand" ):toUtf8(),
groupIndependant=true},
["Kill"] = { menu=i18n.get( "uiR2AA0Kill" ):toUtf8(),
text=i18n.get( "uiR2AA1Kill" ):toUtf8()},
["begin activity sequence"] = { menu=i18n.get( "uiR2AA0BeginSeq" ):toUtf8(),
text=i18n.get( "uiR2AA1BeginSeq" ):toUtf8()},
["Fight with player"] = { menu=i18n.get( "uiR2AA0FlagFightPlayersOn" ):toUtf8(),
text=i18n.get( "uiR2AA1FlagFightPlayersOn" ):toUtf8()},
["Dont fight with player"] = { menu=i18n.get( "uiR2AA0FlagFightPlayersOff" ):toUtf8(),
text=i18n.get( "uiR2AA1FlagFightPlayersOff" ):toUtf8()},
["Fight with Npcs"] = { menu=i18n.get( "uiR2AA0FlagFightNpcsOn" ):toUtf8(),
text=i18n.get( "uiR2AA1FlagFightNpcsOn" ):toUtf8()},
["Dont fight with Npcs"] = { menu=i18n.get( "uiR2AA0FlagFightNpcsOff" ):toUtf8(),
text=i18n.get( "uiR2AA1FlagFightNpcsOff" ):toUtf8()},
["Run"] = { menu=i18n.get( "uiR2AA0FlagRunOn" ):toUtf8(),
text=i18n.get( "uiR2AA1FlagRunOn" ):toUtf8()},
["Dont run"] = { menu=i18n.get( "uiR2AA0FlagRunOff" ):toUtf8(),
text=i18n.get( "uiR2AA1FlagRunOff" ):toUtf8()},
},
["Events"] = {
["activation"] = { menu=i18n.get( "uiR2Event0Spawn" ):toUtf8(),
text=i18n.get( "uiR2Event1Spawn" ):toUtf8()},
["desactivation"] = { menu=i18n.get( "uiR2Event0Despawn" ):toUtf8(),
text=i18n.get( "uiR2Event1Despawn" ):toUtf8()},
["member death"] = { menu=i18n.get( "uiR2Event0MemberDeath" ):toUtf8(),
text=i18n.get( "uiR2Event1MemberDeath" ):toUtf8()},
["group death"] = { menu=i18n.get( "uiR2Event0GroupDeath" ):toUtf8(),
text=i18n.get( "uiR2Event1GroupDeath" ):toUtf8()},
["end of activity step"] = { menu=i18n.get( "uiR2Event0EndActivityStep" ):toUtf8(),
text=i18n.get( "uiR2Event1EndActivityStep" ):toUtf8()},
["end of activity sequence"] = { menu=i18n.get( "uiR2Event0EndActivitySeq" ):toUtf8(),
text=i18n.get( "uiR2Event1EndActivitySeq" ):toUtf8()},
["begin of activity step"] = { menu=i18n.get( "uiR2Event0BeginActivityStep" ):toUtf8(),
text=i18n.get( "uiR2Event1BeginActivityStep" ):toUtf8()},
["begin of activity sequence"] = { menu=i18n.get( "uiR2Event0BeginOfActivitySeq" ):toUtf8(),
text=i18n.get( "uiR2Event1BeginOfActivitySeq" ):toUtf8()},
["targeted by player"] = { menu=i18n.get( "uiR2Event0TargetedByPlayer" ):toUtf8(),
text=i18n.get( "uiR2Event1TargetedByPlayer" ):toUtf8()},
},
["Conditions"] = {
["is active"] = { menu=i18n.get( "uiR2Test0Spawned" ):toUtf8(),
text=i18n.get( "uiR2Test1Spawned" ):toUtf8()},
["is inactive"] = { menu=i18n.get( "uiR2Test0Despawned" ):toUtf8(),
text=i18n.get( "uiR2Test1Despawned" ):toUtf8()},
["is dead"] = { menu=i18n.get( "uiR2Test0Dead" ):toUtf8(),
text=i18n.get( "uiR2Test1Dead" ):toUtf8()},
["is alive"] = { menu=i18n.get( "uiR2Test0Alive" ):toUtf8(),
text=i18n.get( "uiR2Test1Alive" ):toUtf8()},
["is in activity sequence"] = { menu=i18n.get( "uiR2Test0Seq" ):toUtf8(),
text=i18n.get( "uiR2Test1Seq" ):toUtf8()},
["is in activity step"] = { menu=i18n.get( "uiR2Test0Step" ):toUtf8(),
text=i18n.get( "uiR2Test1Step" ):toUtf8()},
}
}
return logicTranslations
end
function component.getActivitiesIds(this)
local activitiesIds = {}
local behavior = this:getBehavior()
local k, v = next(behavior.Activities, nil)
while k do
table.insert(activitiesIds, v.InstanceId)
k, v = next(behavior.Activities, k)
end
return activitiesIds
end
function component.getAiCost(this)
if this.User.GhostDuplicate then return 0 end
return r2.getAiCost(this) - 1
end
-- obsolete
feature.getCost = function (featureInstance)
local cost = 0
local components = featureInstance.Components
local key, comp = next(components, nil)
while(key ~= nil)
do
if (comp.Class == "Npc" or comp.Class == "NpcCustom")
then
cost = cost +1
end
key, comp = next(components, key)
end
return cost
end
return feature
end
r2.Features["NpcGrpFeature"] = registerFeature()
--------------------------------------------------------------------------------------------------
-------------------------- NPC GROUP DisplayerProperties -----------------------------------------
--------------------------------------------------------------------------------------------------
local npcGroupPropertySheetDisplayerTable = clone(r2:propertySheetDisplayer())
------------------------------------------------
function npcGroupPropertySheetDisplayerTable:onPostCreate(instance)
end
------------------------------------------------
function npcGroupPropertySheetDisplayerTable:onErase(instance)
end
------------------------------------------------
function npcGroupPropertySheetDisplayerTable:onPreHrcMove(instance)
end
------------------------------------------------
function npcGroupPropertySheetDisplayerTable:onPostHrcMove(instance)
for i=0, instance.Components.Size-1 do
local entity = instance.Components[i]
entity.DisplayerVisual:updateName()
entity:updatePermanentStatutIcon()
end
r2.events:updateElementsUI()
end
------------------------------------------------
function npcGroupPropertySheetDisplayerTable:onFocus(instance, hasFocus)
end
------------------------------------------------
function npcGroupPropertySheetDisplayerTable:onSelect(instance, isSelected)
r2:activeLogicEntityPropertySheetDisplayer():onSelect(instance, isSelected)
end
------------------------------------------------
function npcGroupPropertySheetDisplayerTable:onAttrModified(instance, attributeName)
r2:activeLogicEntityPropertySheetDisplayer():onAttrModified(instance, attributeName)
end
------------------------------------------------
function r2:npcGroupPropertySheetDisplayer()
return npcGroupPropertySheetDisplayerTable -- returned shared displayer to avoid wasting memory
end