Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local spawn = true
- seagulls = seagulls or {} local s = seagulls
- seagulls.ClassName = "hunting_mod_seagull"
- --seagulls.Nest = Vector(-6637.6226, 7677.0718, -3747.9688) --freesace06
- --seagulls.Nest = Vector(-10385.2158, -11214.9980, 100) --flat constr
- seagulls.Nest = Vector(-805.6716, -7304.4014, 448.0312) -- bigcity
- seagulls.NestRadius = 800
- seagulls.MaxSeagulls = 70
- --seagulls.Min = Vector(14303.9688, -14303.9688, 1)
- --seagulls.Max = Vector(-3048.6169, -3062.0076, 8320.1709)
- do -- precalc
- end
- do -- util
- function seagulls.GetNest()
- return seagulls.Nest
- end
- function seagulls.SetNest(vec)
- seagulls.RoofZ = util.QuickTrace(vec, vector_up * 32000).HitPos.z
- seagulls.Nest = vec
- end
- function seagulls.GetAll()
- return ents.FindByClass(seagulls.ClassName)
- end
- function seagulls.Create()
- if #seagulls.GetAll() < seagulls.MaxSeagulls then
- local ent = ents.Create(seagulls.ClassName)
- ent:SetPos(seagulls.Nest + Vector(math.Rand(-1,1), math.Rand(-1,1), math.Rand(0,1)) * seagulls.NestRadius)
- ent:Spawn()
- return ent
- end
- return NULL
- end
- end
- do -- meta
- local function AccessorFunc(tbl, name, dt)
- tbl["Set" .. name] = function(self, var)
- if dt and self.dt then
- self.dt[name] = var
- else
- self[name] = var
- end
- end
- tbl["Get" .. name] = function(self, var)
- if dt and self.dt then
- return self.dt[name]
- else
- return self[name]
- end
- end
- end
- local ENT = {}
- ENT.IsSeagull = true
- ENT.ClassName = seagulls.ClassName
- ENT.Model = "models/seagull.mdl"
- ENT.Type = "anim"
- ENT.Target = NULL
- ENT.Size = 1
- ENT.Animations = {
- Fly = "fly",
- Run = "run",
- Walk = "walk",
- Idle = "idle01",
- }
- ENT.Sounds = {
- Pain= {
- "ambient/creatures/seagull_pain1.wav",
- "ambient/creatures/seagull_pain2.wav",
- "ambient/creatures/seagull_pain3.wav",
- },
- Idle= {
- "ambient/creatures/seagull_idle1.wav",
- "ambient/creatures/seagull_idle2.wav",
- "ambient/creatures/seagull_idle3.wav",
- },
- AmbientIdle= {
- "ambient/levels/coast/seagulls_ambient1.wav",
- "ambient/levels/coast/seagulls_ambient2.wav",
- "ambient/levels/coast/seagulls_ambient3.wav",
- "ambient/levels/coast/seagulls_ambient4.wav",
- "ambient/levels/coast/seagulls_ambient5.wav",
- },
- Impact= {
- "physics/body/body_medium_impact_soft1.wav",
- "physics/body/body_medium_impact_soft2.wav",
- "physics/body/body_medium_impact_soft3.wav",
- "physics/body/body_medium_impact_soft4.wav",
- "physics/body/body_medium_impact_soft5.wav",
- "physics/body/body_medium_impact_soft6.wav",
- "physics/body/body_medium_impact_soft7.wav",
- },
- }
- AccessorFunc(ENT, "Size", true)
- function ENT:SetupDataTables()
- self:DTVar("Float", 0, "Size")
- self:DTVar("Float", 1, "HeldEntityMass")
- end
- do -- util
- function ENT:PlaySound(type)
- self:EmitSound(
- table.Random(self.Sounds[type]),
- math.Clamp(math.Rand(40, 60) * self:GetSize(), 1, 160),
- math.Clamp(math.random(90,110) / self:GetSize(), 25, 255)
- )
- end
- function ENT:GetBottom(offset)
- offset = offset or 1
- return self:GetPos() + ((vector_up * self:GetSize() * -5) * offset)
- end
- function ENT:GetOnGround()
- if not self.LastOnGround or self.LastOnGround < CurTime() then
- self.OnGroundCache = util.QuickTrace(self:GetBottom(1.1), vector_up * 3, {self, self.HeldEntity, self.FetchEntity}).Hit
- self.LastOnGround = CurTime() + 0.4
- end
- return self.OnGroundCache
- end
- function ENT:GetEntityFilter()
- if not self.LastFilterTime or self.LastFilterTime < CurTime() then
- local pos = self and self:GetPos() or vector_origin -- FIX ME
- self.LastFilter = {self, unpack(ents.FindInSphere(pos, self:GetSize()))}
- self.LastFilterTime = CurTime() + 0.3
- end
- return self.LastFilter
- end
- local mn, mx = seagulls.Min, seagulls.Max
- function seagulls.IsWithinMinMax(p)
- if ms then
- return
- p.x > mn.x and p.y > mn.y and p.z > mn.z and
- p.x < mx.x and p.y < mx.y and p.z < mx.z
- else
- return true
- end
- end
- function ENT:IsWithinMinMax(p)
- return seagulls.IsWithinMinMax(p)
- end
- end
- if CLIENT then
- do -- util
- function ENT:SetAnim(anim)
- self:SetSequence(self:LookupSequence(self.Animations[anim]))
- end
- end
- do -- calc
- ENT.Cycle = 0
- ENT.Noise = 0
- function ENT:AnimationThink(vel, len, ang)
- if self:GetOnGround() then
- if len < 3 then
- self:SetAnim("Idle")
- len = 15 / self:GetSize() * (self.Noise * 2)
- else
- self:StepSoundThink()
- if len > 50 then
- self:SetAnim("Run")
- else
- self:SetAnim("Walk")
- end
- end
- self.ang = Angle(0, ang.y, ang.r)
- self.Noise = (self.Noise + (math.Rand(-1,1) - self.Noise) * FrameTime() * 4)
- else
- self:SetAnim("Fly")
- if vel.z > 0 then
- len = len / 10
- else
- len = 0
- self.Cycle = 0.2
- end
- if self.dt.HeldEntityMass > 0 then
- len = len * self.dt.HeldEntityMass * 0.03
- end
- end
- self.Cycle = self.Cycle + (FrameTime() * len * 0.07)
- self:SetCycle(self.Cycle)
- end
- function ENT:StepSoundThink()
- local stepped = self.Cycle%0.5
- if stepped < 0.3 then
- if not self.stepped then
- self:EmitSound(
- "npc/fast_zombie/foot2.wav",
- math.Clamp(20 * self:GetSize(), 70, 155) + math.Rand(-5,5),
- math.Clamp(100 / (self:GetSize()/2), 40, 200) + math.Rand(-10,10)
- )
- self.stepped = true
- end
- else
- self.stepped = false
- end
- end
- end
- do -- standard
- function ENT:Draw()
- self.vel = self:GetVelocity() / self:GetSize()
- self.len = self.vel:Length()
- self.ang = self.vel:Angle()
- self:AnimationThink(self.vel, self.len, self.ang)
- self:SetModelScale(Vector() * self:GetSize())
- if self.len > 5 or not self.lastang then
- self:SetAngles(self.ang)
- self.lastang = self.ang
- else
- self:SetAngles(self.lastang)
- end
- self:SetRenderOrigin(self:GetBottom())
- local min, max = self:GetRenderBounds()
- local size = self:GetSize() * 2
- self:SetRenderBounds(min * size, max * size)
- self:DrawShadow(false)
- self:SetupBones()
- self:DrawModel()
- self:SetRenderOrigin(nil)
- end
- function ENT:Think()
- if math.random() > 0.999 then
- self:EmitSound(table.Random(self.Sounds.AmbientIdle), 120, math.Clamp(100 / self:GetSize(), 30, 200) + math.Rand(-10,10))
- end
- end
- end
- end
- if SERVER then
- do -- util
- function ENT:GetAverageFlockPos()
- local pos = vector_origin
- local gulls = seagulls.GetAll()
- for _, ent in pairs(gulls) do
- if ent ~= self then
- pos = pos + ent:GetPos()
- end
- end
- local div = #gulls - 1
- if div ~= 0 then
- return pos / div
- end
- return seagulls.Nest
- end
- end
- do -- kill
- function ENT:KillTarget(ent)
- self:SetTargetPos(ent, "kill")
- self.Killing = true
- end
- end
- do -- move
- ENT.SmoothNoise = VectorRand()
- ENT.NextTry = 0
- AccessorFunc(ENT, "MoveDamping")
- AccessorFunc(ENT, "MovePos")
- function ENT:GetPrefferedStopRadius()
- return self.FetchEntity:IsValid() and 10 or 10 * self:GetSize()
- end
- function ENT:StopMoving()
- self.phys:AddVelocity(-self.phys:GetVelocity() * Vector(1,1,0))
- self.stop = true
- end
- function ENT:Trace(a, b)
- if not self.LastTrace or self.LastTrace < CurTime() then
- self.CachedTraceResults = util.TraceLine{
- start = a,
- endpos = b,
- filter = self:GetEntityFilter(),
- }
- self.LastTrace = CurTime() + 0.2
- end
- return self.CachedTraceResults
- end
- function ENT:CanSeePos(pos, threshold)
- return self:Trace(self:GetPos(), pos).HitPos:Distance(pos) < (threshold or 200)
- end
- function ENT:TryHeightPos(pos)
- self:SetMovePos(Vector(pos.x, pos.y, seagulls.RoofZ))
- self.Trying = "height"
- end
- function ENT:TryTracePos(pos)
- local hit_pos = self:Trace(self:GetPos(), pos).HitPos
- if hit_pos:Distance(pos) < 2000 then
- local try = pos + Vector(math.Rand(-1000,1000), math.Rand(-1000,1000), 0)
- if self:CanSeePos(try) then
- self:SetMovePos(try)
- self.Trying = "trace"
- end
- end
- end
- function ENT:SetTargetPos(var, msg)
- local pos = IsVector(var) and var or IsEntity(var) and IsValid(var) and var:GetPos()
- if not pos or pos:Distance(self:GetPos()) < self:GetPrefferedStopRadius() then return end
- self.TargetPos = var
- self.TargetReachMessage = msg
- if self:CanSeePos(pos) then
- self:SetMovePos(pos)
- else
- self:TryHeightPos(pos)
- end
- end
- function ENT:GetTargetPos()
- return self.TargetPos and (IsVector(self.TargetPos) and self.TargetPos or self.TargetPos:GetPos())
- end
- end
- do -- pickup
- ENT.FetchEntity = NULL
- ENT.HeldEntity = NULL
- AccessorFunc(ENT, "HeldEntity")
- function ENT:Pickup(ent)
- if ent:IsValid() and not self.HeldEntity:IsValid() then
- constraint.RemoveAll(ent)
- if ent:IsPlayer() then
- self.HoldingPlayer = true
- elseif ent:IsNPC() then
- ent:SetParent(self)
- else
- --ent:SetPos(self:GetPos())
- local weld = constraint.Weld(self, ent)
- if weld then
- ent:GetPhysicsObject():EnableMotion(true)
- for key, ent in pairs(constraint.GetAllConstrainedEntities(ent)) do
- ent:GetPhysicsObject():EnableMotion(true)
- end
- self.weld = weld
- self:SetHeldEntity(ent)
- self.dt.HeldEntityMass = self:GetPhysicsObject():GetMass()
- ent.seagull_carriers = ent.seagull_carriers or {}
- table.insert(ent.seagull_carriers, self)
- end
- end
- ent:CallOnRemove(seagulls.ClassName, function()
- if self.IsSeagull and self.FetchEntity == ent then
- self:Drop()
- end
- end)
- end
- end
- function ENT:Drop()
- constraint.RemoveAll(self)
- local ent = self.FetchEntity
- if ent:IsValid() then
- if ent:IsPlayer() then
- ent:SetMoveType(MOVETYPE_WALK)
- elseif ent:IsNPC() then
- ent:SetParent()
- else
- ent:SetPos(self.FetchEntity:GetPos())
- if ent.seagull_carriers then
- for key, value in pairs(ent.seagull_carriers) do
- if value.IsSeagull and value ~= self then
- value.DropIt = true
- end
- end
- end
- end
- ent.seagull_lift = nil
- end
- self.dt.HeldEntityMass = 0
- self.FetchEntity = NULL
- self.HeldEntity = NULL
- self.TargetPos = nil
- self.MovePos = nil
- self.HoldingPlayer = false
- end
- function ENT:Fetch(ent)
- if ent:IsValid() and not self.FetchEntity:IsValid() then
- ent.seagull_lift = (ent.seagull_lift or 0) + self:GetSize()
- self.FetchEntity = ent
- self:SetTargetPos(ent, "pickup")
- end
- end
- function ENT:IsPickupAllowed(ent)
- if math.random() > 0.5 then return false end
- if ent == seagulls.Platform then return false end
- if not self:IsWithinMinMax(ent:GetPos()) then
- return false
- end
- if ent:GetPos():Distance(seagulls.Nest) < seagulls.NestRadius then
- return false
- end
- if
- not ent.IsSeagull and
- util.IsValidPhysicsObject(ent) and
- ent:GetMoveType() == MOVETYPE_VPHYSICS and
- (not ent.seagull_lift or (390 * ent.seagull_lift) < ent:GetPhysicsObject():GetMass() + 300) --and
- --not util.QuickTrace(ent:GetPos(), vector_up * 700, ent).Hit
- then
- return true
- end
- if (ent:IsPlayer() and ent:Alive() or ent:IsNPC()) and not ent:GetParent().IsSeagull and not ent.seagull_lift then
- return true
- end
- end
- function ENT:FetchRandom()
- for key, ent in pairs(ents.GetAll()) do
- if self:IsPickupAllowed(ent) then
- self:Fetch(ent)
- break
- end
- end
- end
- end
- do -- calc
- function ENT:IdleThink(phys)
- if not self.TargetPos then
- local vel = (seagulls.Nest - phys:GetPos()) * 0.03
- if self:GetOnGround() then
- if (self.LastIdleDir or 0) < CurTime() then
- phys:AddVelocity(VectorRand() * Vector(1,1,0) * 300)
- self.LastIdleDir = CurTime() + math.Rand(1.5,4)
- end
- end
- if self:GetPos():Distance(seagulls.Nest) > seagulls.NestRadius then
- phys:AddVelocity(seagulls.Nest - phys:GetPos())
- end
- phys:AddVelocity(phys:GetVelocity() *- 0.03)
- end
- end
- function ENT:TryThink()
- if self.NextTry < CurTime() then
- local pos = self:GetTargetPos()
- if pos then
- if self:CanSeePos(pos) then
- self:SetMovePos(pos)
- self.NextTry = 0
- else
- if
- self.Trying == "height" and
- (pos - self.pos):Length2D() < self:GetPrefferedStopRadius()
- then
- self:TryTracePos(pos)
- end
- if
- self.Trying == "trace" and
- (
- self.len < 100 or
- self.pos:Distance(pos) < self:GetPrefferedStopRadius()
- )
- then
- self.Trying = false
- end
- if self.MovePos and not self:CanSeePos(self.MovePos) and self.MovePos:Distance(pos) < 1000 then
- self:TryHeightPos(pos)
- end
- if not self.Trying then
- self:TryHeightPos(pos)
- end
- self.NextTry = CurTime() + math.Rand(0.2,0.4)
- end
- end
- end
- end
- function ENT:MoveThink(phys, pos, movepos, distance)
- local vel = movepos - pos
- phys:AddVelocity(vel:Normalize() * (math.Clamp(vel:Length()*0.1, 30, 500) * self:GetSize()))
- if self.TouchEntity:IsValid() then
- local phys, ent = self.TouchEntity:GetPhysicsObject(), self.TouchEntity
- if ent ~= self.FetchEntity and not ent.seagull_lift and ent ~= seagulls.Platform and phys:IsValid() and ent:GetMoveType() == MOVETYPE_VPHYSICS then
- constraint.RemoveAll(ent)
- phys:EnableMotion(true)
- phys:AddVelocity(VectorRand()*10000)
- local ef = EffectData()
- ef:SetOrigin(ent:GetPos())
- util.Effect("explosion", ef)
- end
- end
- end
- end
- do -- standard
- function ENT:Initialize()
- self:SetSize(math.Rand(0.5, 10.5))
- self:SetModel(self.Model)
- self:PhysicsInitSphere(5 * self:GetSize())
- self:StartMotionController()
- self:SetCollisionGroup(COLLISION_GROUP_INTERACTIVE_DEBRIS)
- self.flight_dir = math.Rand(0.3,1) * (math.random() > 0.5 and -1 or 1)
- local phys = self:GetPhysicsObject()
- phys:SetMass(20 * self:GetSize())
- phys:SetMaterial("default_silent")
- self.phys = phys
- end
- function ENT:Think()
- self:PhysWake()
- self:FetchRandom()
- self:NextThink(CurTime() + math.Rand(1,5))
- return true
- end
- function ENT:PhysicsSimulate(phys, delta)
- self.vel = phys:GetVelocity()
- self.len = self.vel:Length()
- self.len2d = self.vel:Length2D()
- self.pos = phys:GetPos()
- if self.DropIt then
- self:Drop()
- self.DropIt = false
- end
- if not self.FetchEntity:IsValid() then
- self:Drop()
- end
- if ms and self.FetchEntity:IsValid() and not self:IsWithinMinMax(self.FetchEntity:GetPos()) then
- self:Drop()
- end
- if self.HoldingPlayer then
- self.FetchEntity:SetMoveType(MOVETYPE_NOCLIP)
- self.FetchEntity:GetPhysicsObject():SetVelocity(vector_origin)
- self.FetchEntity:SetVelocity(vector_origin)
- self.FetchEntity:SetPos(self:GetPos())
- end
- local reached_message
- if self:GetOnGround() then
- self.MoveDamping = 0.4
- elseif self.len2d > 10 then
- self.MoveDamping = 0.1
- phys:AddVelocity(vector_up * (self.len2d * 0.003))
- end
- if self.MovePos then
- self:TryThink()
- self.MoveDistance = (self.MovePos-phys:GetPos()):LengthSqr()
- if
- IsVector(self.TargetPos) and (self:GetTargetPos()-self.pos):LengthSqr() < 1300 or
- IsEntity(self.TargetPos) and (self.TouchEntity == self.TargetPos or (self:GetTargetPos()-self.pos):LengthSqr() < 10)
- then
- reached_message = self.TargetReachMessage
- end
- if self.MoveDistance < self:GetPrefferedStopRadius()*100 then
- self:StopMoving()
- else
- self:MoveThink(phys, self.pos, self.MovePos, self.MoveDistance)
- phys:AddVelocity(-phys:GetVelocity() * self:GetMoveDamping() * math.min(self.len2d / 1000, 1000))
- phys:AddVelocity(self.SmoothNoise * Vector(1,1,0.5) * self:GetSize() * 30)
- end
- end
- if not self.Killing then
- self:IdleThink(phys)
- end
- if reached_message == "pickup" then
- timer.Simple(0, function()
- self:Pickup(self.FetchEntity)
- self:SetTargetPos(seagulls.Nest, "nest")
- end)
- elseif reached_message == "nest idle" then
- self.TargetPos = nil
- self.MovePos = nil
- --self:FetchRandom()
- elseif reached_message == "kill" then
- if self.TargetPos:IsPlayer() then
- self.TargetPos:TakeDamage(self:GetSize() * 10)
- if not self.TargetPos:Alive() then
- self.Killing = false
- end
- end
- elseif reached_message == "nest" then
- self:Drop()
- end
- --[[ if self.FetchEntity:IsPlayer() then
- if self.FetchEntity:GetMoveType() ~= MOVETYPE_WALK then
- self.FetchEntity:SetMoveType(MOVETYPE_WALK)
- end
- if self.FetchEntity:GetPos():Distance(self:GetPos()) < 5000 then
- self.FetchEntity.seagull_noclip_disallowed = self
- else
- self.FetchEntity.seagull_noclip_disallowed = nil
- end
- end ]]
- self.Stop = false
- self.SmoothNoise = self.SmoothNoise + (((VectorRand() * 0.1) - self.SmoothNoise) * FrameTime())
- end
- ENT.HP = 100
- function ENT:OnTakeDamage(dmg)
- local ply = dmg:GetAttacker()
- local allowed = (dmg:GetInflictor():IsPlayer() and dmg:GetAttacker():IsPlayer())
- if not allowed then
- print("seagull coin drop dissallowed", dmg:GetInflictor(), dmg:GetAttacker(), dmg:GetDamageType())
- self.DontSpawnCoin = true
- end
- if ply:IsPlayer() then
- self:KillTarget(ply)
- end
- self.HP = self.HP - (dmg:GetDamage() / (self:GetSize()*0.3))
- for i=1, 100 do self:EmitSound(table.Random(self.Sounds.Pain), 160, 100) end
- if self.HP < 0 then
- self:Die()
- timer.Simple(math.random(10), function()
- seagulls.Create()
- end)
- end
- end
- ENT.TouchEntity = NULL
- function ENT:StartTouch(ent)
- self.TouchEntity = ent
- end
- function ENT:EndTouch(...)
- self.TouchEntity = NULL
- end
- function ENT:PhysicsCollide(data)
- self.TouchEntity = data.HitEntity
- end
- function ENT:Die()
- if not self.DontSpawnCoin and coins then
- local coin = coins.Create(self:GetPos(), math.floor(self:GetSize()*100))
- coin:GetPhysicsObject():AddVelocity(VectorRand()*150)
- end
- self:Remove()
- self:PlaySound("Impact")
- end
- function ENT:OnRemove()
- self:Drop()
- end
- end
- end
- scripted_ents.Register(ENT, s.ClassName, true)
- seagulls.EntityMeta = ENT
- end
- do -- "external" hooks
- function seagulls.Call(name, ...)
- for key, ent in pairs(ents.FindByClass(seagulls.ClassName)) do
- if type(ent[name]) == "function" then
- ent[name](ent, ...)
- end
- end
- end
- function seagulls.EntHook(name)
- hook.Add(name, seagulls.ClassName .. "_hook", function(...)
- seagulls.Call(name, ...)
- end)
- end
- function seagulls.Hook(name, func)
- hook.Add(name, seagulls.ClassName .. "_hook", func or seagulls[name])
- end
- function seagulls.DissallowPickup(ply, ent)
- if ent.IsSeagull then
- return false
- end
- end
- seagulls.Hook("PhysgunDrop", seagulls.DissallowPickup)
- seagulls.Hook("PhysgunPickup", seagulls.DissallowPickup)
- function seagulls.PlayerNoClip(ply)
- --if IsValid(ply.seagull_noclip_disallowed) then
- -- return false
- --end
- if seagulls.IsWithinMinMax(ply:GetPos()) then
- return false
- end
- end
- seagulls.Hook("PlayerNoClip")
- function seagulls.PlayerDeath(ply,ent)
- if coins and ent.IsSeagull then
- ply:DropCoins(ent:GetSize()*50)
- ply:PayCoins(ent:GetSize()*50)
- ply:SetParent()
- end
- end
- seagulls.Hook("PlayerDeath")
- function seagulls.PlayerShouldTakeDamage(ply, ent)
- if ent.IsSeagull then
- return true
- end
- end
- seagulls.Hook("PlayerShouldTakeDamage")
- if SERVER then
- function seagulls.PlayerInitialSpawn()
- seagulls.RoofZ = util.QuickTrace(seagulls.Nest, vector_up * 32000).HitPos.z - 300
- if IsValid(seagulls.Platform) then seagulls.Platform:Remove() end
- seagulls.Platform = "models/props_combine/CombineInnerwallCluster1024_001a.mdl"
- local platform = ents.Create("prop_physics")
- platform:SetModel(seagulls.Platform)
- platform:SetPos(seagulls.Nest + Vector(0,0,-200))
- platform:Spawn()
- platform:PhysicsInit(SOLID_VPHYSICS)
- platform:SetSolid(SOLID_VPHYSICS)
- platform:SetMoveType(MOVETYPE_VPHYSICS)
- platform:GetPhysicsObject():EnableMotion(false)
- platform:SetAngles(Angle(-80,0,0))
- constraint.Weld(platform, GetWorldEntity())
- seagulls.Platform = platform
- for key, value in pairs(seagulls.GetAll()) do
- value:Remove()
- end
- for key, value in pairs(ents.GetAll()) do
- value.seagull_lift = nil
- end
- timer.Simple(0.2, function()
- for i=1, 50 do
- seagulls.Create()
- end
- end)
- hook.Remove("PlayerInitialSpawn", seagulls.ClassName .. "_hook")
- end
- seagulls.Hook("PlayerInitialSpawn")
- timer.Create("seagulls", 1000, 1, seagulls.PlayerInitialSpawn)
- end
- end
- seagulls.PlayerInitialSpawn()
Advertisement
Add Comment
Please, Sign In to add comment