File size: 9,636 Bytes
b6a38d7 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 |
-- 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 |