myspace / Lua /Canvas.lua
sirnii's picture
Upload 1816 files
b6a38d7 verified
raw
history blame
9.64 kB
-- Swaying of all canvas objects depends on the wind slot they lie in. Each slot has wind direction and strength.
-- Wind strength can be strong or weak depending on const.StrongWindThreshold which is % of the const.WindMaxStrength
-- absolute value. Canvasses have different sway animation for weak and for strong wind. They also have 2 two different
-- animations depending on whether they are placed "Next to Wall" or "Freely Sway"("Sway Type" property).
-- This makes for total of 4 animations. There is also "Never Sway" option for that property which just places the object in
-- "idle" state. The animations have these strange names depending on wind strength/sway type:
--
-- - Strong Wind:
-- * "Next to Wall" - idle_Wind
-- * "Freely Sway" - idle_Wind_No_Wall
-- - Weak Wind:
-- * "Next to Wall" - idle_Wall
-- * "Freely Sway" - idle_No_Wall
--
-- In case the Canvas object is SlabWallWindow it can be also either broken or intact. For its broken version the above
-- animations are using "broken_" as prefix instead of "idle_".
--
-- Changing light model or weather(via GameState) forces updating of the wind animations of Canvas objects. If the object is
-- not "Never Sway" its animation is choosen using the table above. For windows there is additional chance of not swaying
-- even when "Sway Type" is not set to "Never Sway". This chance is different for weak and strong wind and is defined by
-- const.WindStrongSwayChance and const.WindWeakSwayChance. Anyway the canvasses can sway only if they lie on the grid
-- with some positive wind in their slot.
--
-- In the case of weak wind the corresponding canvas animation is blended with the idle/broken base animation using
-- wind strength as animation weight factor. In case of strong wind no blending is executed. Blending can be disabled
-- per entity from Entity Spec Editor using the "Disable Canvas Wind Blending" property.
-- If during the update the animation is changed its phase is randomized so the objects are not synced.
--
-- FX-es are fired upon changing animations, e.g.:
-- WindWeak-start/end-Window0_1-Wall
-- WindStrong-end-MilitaryFlag_01-No Wall
--
--
-- NOTE: currently const.StrongWindThreshold is 100%.
DefineClass.Canvas = {
__parents = {"WindAffected", "Object", "PropertyObject"},
flags = {gofRealTimeAnim = true},
properties = {
{category = "Canvas", id = "SwayType", name = "Sway Type", editor = "dropdownlist",
items = {"Never Sway", "Next To Wall", "Freely Sway"}, default = "Freely Sway"},
{category = "Canvas", id = "StateText", editor = "combo", default = "idle",
items = function(obj) return obj:GetStatesTextTable(obj.StateCategory) end,
OnStartEdit = function(obj) obj:SetRealtimeAnim(true) end,
OnStopEdit = function(obj) obj:SetRealtimeAnim(false) end,
buttons = {{name = "Play once", func = "BtnTestOnce"}, {name = "Loop", func = "BtnTestLoop"}, {name = "Test", func = "BtnTestState"}}, dont_save = true,
no_edit = true, dont_save = true,
},
},
fx_actor_class = "Canvas",
}
function Canvas:GetBaseWindState()
local base_state = "idle"
if IsKindOf(self, "SlabWallWindow") then
if self.is_destroyed then
local _
_, base_state = self:GetDestroyedEntityAndState()
else
base_state = self:IsBroken() and "broken" or base_state
end
end
if not IsValidAnim(self, base_state) then
StoreErrorSource(self, string.format("Canvas window does not have '%s' animation, falling back to 'idle'", base_state))
base_state = "idle"
end
return base_state
end
function Canvas:RandomizePhase(second_channel)
local duration = GetAnimDuration(self:GetEntity(), self:GetState())
local phase = self:Random(duration)
self:SetAnimPhase(1, phase)
if second_channel then
self:SetAnimPhase(2, phase)
end
end
function Canvas:GetWindAnim(wind_state, base_state)
local anim = string.format(wind_state, base_state)
if not IsValidAnim(self, anim) then
StoreWarningSource(self, string.format("Canvas object without wind animation '%s', falling back to '%s'", anim, base_state))
return base_state
end
return anim
end
local strong_chance = const.WindStrongSwayChance
local weak_chance = const.WindWeakSwayChance
function Canvas:ShouldSway()
local should_sway = self.SwayType ~= "Never Sway" and self.SwayType ~= "Never Sway Broken"
return should_sway and self:GetWindStrength() > 0
end
function Canvas:UpdateWind(sync)
local base_state = self:GetBaseWindState()
if not self:ShouldSway(sync) then
if base_state == "idle" then
if self:HasState("idle_Static") then
self:ClearAnim(2)
self:SetState("idle_Static")
else
--StoreWarningSource(self, string.format("Canvas window does not have idle_Static animation, falling back to 'idle'"))
self:SetState(base_state)
end
else
self:SetState(base_state)
end
return
end
local entity_data = EntityData[self.class] and EntityData[self.class].entity
local wind_blending_disabled = entity_data.DisableCanvasWindBlending or self:IsStaticAnim(GetStateIdx(base_state))
local state = self:GetStateText()
if self:IsStrongWind() then
wind_blending_disabled = true
if self.SwayType == "Next To Wall" or self.SwayType == "Next To Wall Broken" then
local anim = self:GetWindAnim("%s_Wind", base_state)
if state ~= anim then
PlayFX("WindWeak", "end", self, "Wall")
self:SetState(anim)
self:RandomizePhase()
PlayFX("WindStrong", "start", self, "Wall")
end
elseif self.SwayType == "Freely Sway" then
local anim = self:GetWindAnim("%s_Wind_No_Wall", base_state)
if state ~= anim then
PlayFX("WindWeak", "end", self, "No Wall")
self:SetState(anim)
self:RandomizePhase()
PlayFX("WindStrong", "start", self, "No Wall")
end
end
else
if self.SwayType == "Next To Wall" or self.SwayType == "Next To Wall Broken" then
local anim = self:GetWindAnim("%s_Wall", base_state)
if state ~= anim then
PlayFX("WindStrong", "end", self, "Wall")
self:SetAnim(1, anim)
self:SetAnim(2, base_state)
self:RandomizePhase(true)
PlayFX("Blend WindWeak", "start", self, "Wall")
end
elseif self.SwayType == "Freely Sway" then
local anim = self:GetWindAnim("%s_No_Wall", base_state)
if state ~= anim then
PlayFX("WindStrong", "end", self, "No Wall")
self:SetAnim(1, anim)
self:SetAnim(2, base_state)
self:RandomizePhase(true)
PlayFX("WindWeak", "start", self, "No Wall")
end
end
-- change the weighting
if wind_blending_disabled then
if not self:IsStaticAnim(self:GetState()) then
self:SetAnimWeight(1, 100)
self:SetAnimWeight(2, 0)
end
else
local strong_wind_threshold = GetStrongWindThreshold()
local wind_strength = self:GetWindStrength()
assert(wind_strength <= strong_wind_threshold)
local wind_anim_weight = Max(MulDivTrunc(wind_strength, 100, strong_wind_threshold), 1)
self:SetAnimWeight(1, wind_anim_weight)
self:SetAnimWeight(2, 100 - wind_anim_weight)
end
self:RandomizePhase(self:GetAnim(2) > 0)
end
end
function Canvas:OnEditorSetProperty(prop_id)
if prop_id == "SwayType" then
self:UpdateWind()
end
end
DefineClass.CanvasNextToWallOnly = {
__parents = {"Canvas"},
properties = {
{category = "Canvas", id = "SwayType", name = "Sway Type", editor = "dropdownlist", read_only = true,
items = {"Never Sway", "Never Sway Broken", "Next To Wall"}, default = "Next To Wall"},
},
}
DefineClass.CanvasWindow = {
__parents = {"CanvasNextToWallOnly", "SlabWallWindow", "AutoAttachCallback"},
properties = {
{category = "Canvas", id = "SwayType", name = "Sway Type", editor = "dropdownlist", default = "Next To Wall",
items = {"Never Sway", "Never Sway Broken", "Next To Wall", "Next To Wall Broken"}
},
},
}
function CanvasWindow:PostLoad()
self:SetProperState()
end
function CanvasWindow:ShouldSway(sync)
if not Canvas.ShouldSway(self, sync) then
return false
end
local rand = sync and InteractionRand(100) or AsyncRand(100)
return rand < (self:IsStrongWind() and strong_chance or weak_chance)
end
function CanvasWindow:OnAttachToParent(parent, spot)
self:SetProperty("SwayType", "Never Sway")
self:SetProperState()
end
function CanvasWindow:SetWindowState(window_state, no_fx)
if self.pass_through_state == "intact" and window_state == "broken" then
self:SetState("idle")
if not no_fx then
PlayFX("WindowBreak", "start", self)
end
end
self.pass_through_state = window_state
self:UpdateWind()
end
function CanvasWindow:SetProperState()
local broken = self.SwayType == "Next To Wall Broken" or self.SwayType == "Never Sway Broken"
local state = broken and "broken" or "idle"
local wind = self:IsStrongWind() and "Wind" or "Wall"
local anim = string.format("%s_%s", state, wind)
self.pass_through_state = broken and "broken" or "intact"
if self.pass_through_state == "intact" and IsKindOf(self, "SlabWallWindowOpen") then
self.pass_through_state = "open"
end
self:SetState(IsValidAnim(self, anim) and anim or state)
end
function CanvasWindow:OnEditorSetProperty(prop_id)
if prop_id == "SwayType" then
self:SetProperState()
self:UpdateWind()
end
end
DefineClass.CanvasWindowWindStateFallback = {
__parents = {"CanvasWindow"},
}
function CanvasWindowWindStateFallback:GetWindAnim(wind_state, base_state)
local anim = string.format(wind_state, base_state)
if not IsValidAnim(self, anim) then
return base_state
end
return anim
end
DefineClass.MilitaryCamp_LegionFlag_Short = {
__parents = {"Canvas"}
}
local offset = point(3 * guim, 0, 0)
function MilitaryCamp_LegionFlag_Short:GetWindSamplePos()
return self:GetPos() + Rotate(offset, self:GetAngle())
end