--[[ | This file was obtained through the combined efforts | of Madbluntz & Plymouth Antiquarian Society. | | Credits: lifestorm, Gregory Wayne Rossel JR., | Maloy, DrPepper10 @ RIP, Atle! | | Visit for more: https://plymouth.thetwilightzone.ru/ --]] AddCSLuaFile() ENT.Type = "anim" ENT.AutomaticFrameAdvance = true function ENT:SetupDataTables() self:NetworkVar("String", 0, "PillForm") self:NetworkVar("Entity", 0, "PillUser") --self:NetworkVar("Int",0,"PillHealth") self:NetworkVar("Entity", 1, "PillAimEnt") end function ENT:SetPillHealth(h) local ply = self:GetPillUser() if IsValid(ply) then ply:SetHealth(h) end end function ENT:GetPillHealth(h) local ply = self:GetPillUser() if IsValid(ply) then return ply:Health() end end function ENT:Initialize() pk_pills.mapEnt(self:GetPillUser(), self) self.formTable = pk_pills.getPillTable(self:GetPillForm()) local ply = self:GetPillUser() if not self.formTable or not IsValid(ply) then self:Remove() return end ply:SetRenderMode(RENDERMODE_NONE) if SERVER then local model = self.formTable.model local skin = self.formTable.skin local visMat = self.formTable.visMat if self.formTable.options then local options = self.formTable.options() if self.option and options[self.option] then local pickedOption = options[self.option] if pickedOption.model then model = pickedOption.model end if pickedOption.skin then skin = pickedOption.skin end if pickedOption.visMat then visMat = pickedOption.visMat end else local pickedOption = table.Random(options) if not model then model = pickedOption.model end if not skin then skin = pickedOption.skin end if not visMat then visMat = pickedOption.visMat end end end self:SetModel(model or "models/props_junk/watermelon01.mdl") if skin then self:SetSkin(skin) end --Physics if self.formTable.sphericalPhysics then self:PhysicsInitSphere(self.formTable.sphericalPhysics) self:GetPhysicsObject():SetMass(250) elseif self.formTable.boxPhysics then self:PhysicsInitBox(self.formTable.boxPhysics[1], self.formTable.boxPhysics[2]) self:GetPhysicsObject():SetMass(250) else self:PhysicsInit(SOLID_VPHYSICS) self:SetMoveType(MOVETYPE_VPHYSICS) self:SetSolid(SOLID_VPHYSICS) end if self.formTable.spawnFrozen then self:GetPhysicsObject():EnableMotion(false) end ply:DeleteOnRemove(self) --ply:PhysicsInit(SOLID_NONE) ply:SetMoveType(MOVETYPE_NONE) ply:SetSolid(SOLID_NONE) ply:GodEnable() ply:SetArmor(0) ply:StripWeapons() ply:RemoveAllAmmo() if ply:FlashlightIsOn() then ply:Flashlight(false) end pk_pills.setAiTeam(ply, "harmless") if self.formTable.health then ply:SetHealth(self.formTable.health) ply:SetMaxHealth(self.formTable.health) end local phys = self:GetPhysicsObject() if IsValid(phys) then phys:Wake() if self.formTable.physMat then phys:SetMaterial(self.formTable.physMat) end end self.drive = { functions = pk_pills.getDrive(self.formTable.driveType), options = self.formTable.driveOptions } self.loopingSounds = {} if self.formTable.sounds then for k, v in pairs(self.formTable.sounds) do if v == false then continue end if string.sub(k, 1, 5) == "loop_" then self.loopingSounds[string.sub(k, 6)] = CreateSound(self, v) elseif string.sub(k, 1, 5) == "auto_" and isstring(v) then local func func = function() if IsValid(self) then local f = self.formTable.sounds[k .. "_func"] if not f then return end local play, time = f(ply, self) if play then self:PillSound(k) end timer.Simple(time, func) end end func() end end if self.loopingSounds.move and not self.formTable.moveSoundControl then self:PillLoopSound("move") end end --[[if self.formTable.health then self:SetPillHealth(self.formTable.health) end]] if self.formTable.seqInit then self:PillAnim(self.formTable.seqInit, true) end if visMat then self:SetMaterial(visMat) end if self.formTable.subMats then for k, v in pairs(self.formTable.subMats) do self:SetSubMaterial(k, v) end end if self.formTable.modelScale then self:SetModelScale(self.formTable.modelScale, .1) end if self.formTable.trail then local t = self.formTable.trail local start = t.width or 40 local endd = start / 2 util.SpriteTrail(self, 0, t.color or Color(255, 255, 255), false, start, endd, t.length or 4, 1 / (start + endd) * .5, t.texture) end if self.formTable.bodyGroups then for _, v in pairs(self.formTable.bodyGroups) do self:SetBodygroup(v, 1) end end local mins, maxs = self:GetCollisionBounds() local points = {Vector(mins.x, 0, 0), Vector(0, mins.y, 0), Vector(0, 0, mins.z), Vector(maxs.x, 0, 0), Vector(0, maxs.y, 0), Vector(0, 0, maxs.z)} self:AddFlags(FL_OBJECT) pk_pills.setAiTeam(self, self.formTable.side or "default") self:SetOwner(ply) --self:SetPhysicsAttacker(ply) self:SetPlaybackRate(1) else if ply == LocalPlayer() then --Compatibility with gm+ if gmp and gmp.Enabled:GetBool() then ply.pk_pill_gmpEnabled = true --RunConsoleCommand("gmp_enabled","0") end --Compatibility with Gmod Legs --ply.ShouldDisableLegs=true self.camTraceFilter = {self} end end end function ENT:PhysicsUpdate() if SERVER and self.drive.functions then self.drive.functions.think(self:GetPillUser(), self, self.drive.options) end end function ENT:OnRemove() local ply = self:GetPillUser() if SERVER then self:PillLoopStopAll() end if not pk_pills.unmapEnt(self:GetPillUser(), self) then if SERVER then pk_pills.setAiTeam(ply, "default") ply:SetRenderMode(RENDERMODE_NORMAL) if not self.dead then local angs = ply:EyeAngles() ply:Spawn() ply:SetEyeAngles(angs) ply:SetPos(self:GetPos()) local phys = self:GetPhysicsObject() if phys:IsValid() then ply:SetVelocity(phys:GetVelocity()) end else ply:KillSilent() end else if ply == LocalPlayer() then if ply.pk_pill_gmpEnabled then ply.pk_pill_gmpEnabled = nil end end end end end if CLIENT then --Clientside think hook-used for pose params function ENT:Think() if self.formTable then if self.formTable.renderOffset then local offset = self.formTable.renderOffset(self:GetPillUser(), self) self:SetRenderOrigin(self:GetNetworkOrigin() + offset) end end self:NextThink(CurTime()) return true end else function ENT:Think() local ply = self:GetPillUser() if not IsValid(ply) then return end ply:SetPos(self:GetPos()) if self.formTable.pose then for k, f in pairs(self.formTable.pose) do local old = self:GetPoseParameter(k) local new = f(self:GetPillUser(), self, old) if new then self:SetPoseParameter(k, new) end end end if self.formTable.aim and not self.formTable.aim.usesSecondaryEnt or IsValid(self:GetPillAimEnt()) then local aimEnt = self.formTable.aim.usesSecondaryEnt and self:GetPillAimEnt() or self if self.formTable.aim.xPose and self.formTable.aim.yPose then if not self.formTable.canAim or self.formTable.canAim(ply, self) then local ang = ply:EyeAngles() local locang = aimEnt:WorldToLocalAngles(ang) local xOffset = self.formTable.aim.xOffset or 0 if not self.formTable.aim.xInvert then aimEnt:SetPoseParameter(self.formTable.aim.xPose, math.NormalizeAngle(locang.y + xOffset)) else aimEnt:SetPoseParameter(self.formTable.aim.xPose, math.NormalizeAngle(-locang.y + xOffset)) end if not self.formTable.aim.yInvert then aimEnt:SetPoseParameter(self.formTable.aim.yPose, locang.p) else aimEnt:SetPoseParameter(self.formTable.aim.yPose, -locang.p) end elseif not self.formTable.useDefAim or self.formTable.useDefAim(ply, self) then aimEnt:SetPoseParameter(self.formTable.aim.xPose, self.formTable.aim.xDef or 0) aimEnt:SetPoseParameter(self.formTable.aim.yPose, 0) end end end if self.formTable.boneMorphs then for k, v in pairs(self.formTable.boneMorphs) do local b = self:LookupBone(k) if b then if isfunction(v) then v = v(ply, self) end if not istable(v) then continue end if v.pos then self:ManipulateBonePosition(b, v.pos) end if v.rot then self:ManipulateBoneAngles(b, v.rot) end if v.scale then self:ManipulateBoneScale(b, v.scale) end end end end if self.formTable.moveSoundControl then local p = self.formTable.moveSoundControl(ply, self) if p then self:PillLoopSound("move", nil, p) else self:PillLoopStop("move") end end if (self.formTable.damageFromWater and self:WaterLevel() > 1) then if self.formTable.damageFromWater == -1 then self:PillDie() else --TODO APPLY DAMAGE end end if ply:KeyDown(IN_ATTACK) and self.formTable.attack and self.formTable.attack.mode == "auto" then if not self.formTable.aim or not self.formTable.aim.usesSecondaryEnt or IsValid(self:GetPillAimEnt()) then self:PillLoopSound("attack") else self:PillLoopStop("attack") end if (not self.lastAttack or self.formTable.attack.delay < CurTime() - self.lastAttack) then self.formTable.attack.func(ply, self, self.formTable.attack) self.lastAttack = CurTime() end else self:PillLoopStop("attack") end if ply:KeyDown(IN_ATTACK2) and self.formTable.attack2 and self.formTable.attack2.mode == "auto" then if not self.formTable.aim or not self.formTable.aim.usesSecondaryEnt or IsValid(self:GetPillAimEnt()) then self:PillLoopSound("attack2") else self:PillLoopStop("attack2") end if not self.lastAttack2 or self.formTable.attack2.delay < CurTime() - self.lastAttack2 then self.formTable.attack2.func(ply, self, self.formTable.attack2) self.lastAttack2 = CurTime() end else self:PillLoopStop("attack2") end self:NextThink(CurTime()) return true end function ENT:DoKeyPress(ply, key) if self.drive.functions then self.drive.functions.key(ply, self, self.drive.options, key) end if key == IN_ATTACK and self.formTable.attack and self.formTable.attack.mode == "trigger" then self.formTable.attack.func(ply, self, self.formTable.attack) end if key == IN_ATTACK2 and self.formTable.attack2 and self.formTable.attack2.mode == "trigger" then self.formTable.attack2.func(ply, self, self.formTable.attack2) end if key == IN_RELOAD and self.formTable.reload then self.formTable.reload(ply, self) end end function ENT:StartTouch(TouchEnt) if not IsValid(TouchEnt:GetPhysicsObject()) then return end if (TouchEnt:IsPlayer() or TouchEnt:IsNPC() or TouchEnt:GetClass() == "pill_ent_phys") and TouchEnt:GetPhysicsObject():GetMaterial() ~= "metal" then if self.formTable.contact then local dmg_amt, dmg_type, dmg_force = self.formTable.contact(self:GetPillUser(), self, TouchEnt) if dmg_amt then local dmg = DamageInfo() dmg:SetDamage(20) dmg:SetDamagePosition(self:GetPos()) dmg:SetAttacker(self:GetPillUser()) if dmg_type then dmg:SetDamageType(dmg_type) end if dmg_force then local force_vector = (TouchEnt:GetPos() - self:GetPos()) if self.formTable.contactForceHorizontal then force_vector.z = 0 end force_vector:Normalize() force_vector = force_vector * dmg_force dmg:SetDamageForce(force_vector) self:GetPhysicsObject():ApplyForceCenter(-force_vector) end TouchEnt:TakeDamageInfo(dmg) self:PillSound("contact") end end end end function ENT:PhysicsCollide(collide, phys) if self.formTable.collide then self.formTable.collide(self:GetPillUser(), self, collide) end end function ENT:OnTakeDamage(dmg) if (self.formTable.diesOnExplode and dmg:GetDamageType() == DMG_BLAST) then self:PillDie() end if self.formTable.health then if not self.formTable.onlyTakesExplosiveDamage then local newHealth = self:GetPillHealth() - dmg:GetDamage() if newHealth <= 0 then self:PillDie() else self:SetPillHealth(newHealth) end elseif dmg:GetDamageType() == DMG_BLAST then local newHealth = self:GetPillHealth() - 1 if newHealth <= 0 then self:PillDie() else self:SetPillHealth(newHealth) end end end end function ENT:PillDie() if self.dead then return end if self.formTable.die then self.formTable.die(self:GetPillUser(), self) end self:GetPillUser():SetPos(self:GetPos()) --This makes the post-death camera position correctly self:PillSound("die") self:Remove() self.dead = true pk_pills.handeDeathCommon(self) end function ENT:PillAnim(seq, force) if force then --self:ResetSequenceInfo() self:ResetSequence(self:LookupSequence(seq)) else self:SetSequence(self:LookupSequence(seq)) end end function ENT:PillFilterCam(ent) net.Start("pk_pill_filtercam") net.WriteEntity(self) net.WriteEntity(ent) net.Send(self:GetPillUser()) end end --The "bulk" parameter should only be used if you plan to play a ton of sounds in quick succession. function ENT:PillSound(name, bulk) if not self.formTable.sounds then return end local s = self.formTable.sounds[name] if (istable(s)) then s = table.Random(s) end if isstring(s) then if bulk then sound.Play(s, self:GetPos(), self.formTable.sounds[name .. "_level"] or 100, self.formTable.sounds[name .. "_pitch"] or 100, 1) else self:EmitSound(s, self.formTable.sounds[name .. "_level"] or 100, self.formTable.sounds[name .. "_pitch"] or 100) end return true end end function ENT:PillLoopSound(name, volume, pitch) if not self.loopingSounds[name] then return end local s = self.loopingSounds[name] if s:IsPlaying() then if volume then s:ChangeVolume(volume) end if pitch then s:ChangePitch(pitch, .1) end else if volume or pitch then s:PlayEx(volume or 1, pitch or 100) else s:Play() end end end function ENT:PillLoopStop(name) if not self.loopingSounds[name] then return end local s = self.loopingSounds[name] s:FadeOut(.1) end function ENT:PillLoopStopAll() if not self.loopingSounds then return end for _, v in pairs(self.loopingSounds) do v:Stop() end end function ENT:Draw() if self:GetPillUser() ~= LocalPlayer() or pk_pills.convars.cl_thirdperson:GetBool() then if self.formTable.sprite then if not self.spriteMat then self.spriteMat = Material(self.formTable.sprite.mat) end local size = self.formTable.sprite.size or 40 cam.Start3D(EyePos(), EyeAngles()) render.SetMaterial(self.spriteMat) render.DrawSprite(self:GetPos() + (self.formTable.sprite.offset or Vector(0, 0, 0)), size, size, self.formTable.sprite.color or Color(255, 255, 255)) cam.End3D() else self:DrawModel() end end end