Water (again)

FehaFeha Join Date: 2006-11-16 Member: 58633Members
edited February 2011 in Modding
<div class="IPBDescription">a water trigger that mappers can use</div>I made water before, but then it was just a sphere placed with rifle, and swimming easily made oyu stuck in roof. But thanks to max latest q&a I now know how to add entities into the spark editor!

So I have changed the shape of the water into that of a trigger (basiclly a brush), which mappers can place and scale and such.

Please note that there currently is alot of odd glitches with how the trigger entity works, atleast when it comes to players, if anyone know how to solve them please tell me.
Examples being the enter/exit hooks running several times without the opposite running inbetween, or how you can exit a trigger and yet not have the exit hook run.

I have not made any graphics for the water, so it will be invisible ingame. While in editor its a white box.

My test map was a simple box with a ready room spawn, a map_extents, an ambient light, and 2 water, one shallow and one tall (with manually created faces to tell me where each started/ended).


The swimming is implemented inside player.lua, and I will release the whole file instead of only my changes. I have however added " - by feha" at the end of the first comment in each such section. So if you want to read my code just search for that.
<!--coloro:#FF0000--><span style="color:#FF0000"><!--/coloro--><!--sizeo:3--><span style="font-size:12pt;line-height:100%"><!--/sizeo--><b>The player.lua is a huge file, so you might want to just grab the slider and drag past it!</b><!--sizec--></span><!--/sizec--><!--colorc--></span><!--/colorc-->

Water.lua
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->// ======= Copyright stuff, I give ppl permission to use this hoever they want, altough preferably with putting me in the credits =======
//
// lua\Water.lua
//
//    By Feha
//
//It is supposed to sort of mimic how water brushes in source works
//
// ========= For more information, visit http://www.unknownworlds.com =====================
class 'Water' (Trigger)

Water.kMapName = "water"

function Water:OnInit()
    
    Trigger.OnInit(self)
    
    self.physicsBody:SetCollisionEnabled(true)
    
end

function Water:OnTriggerEntered(enterEnt, triggerEnt)
    
    --Only affect players so far
    if enterEnt:isa("Player") then
        
        --Offset so you cant swim with only feet in water
        entOrigin = enterEnt:GetOrigin() + Vector(0,0,3)
        if (self:GetIsPointInside(entOrigin)) then
            enterEnt.inWater = self:GetName()
        end
        
    end
        
end

function Water:OnTriggerExited(exitEnt, triggerEnt)
    
    if exitEnt:isa("Player") then
        
        entOrigin = exitEnt:GetOrigin() + Vector(0,0,3)
        if (not self:GetIsPointInside(entOrigin)) then
            --Names is used as when you enter a new water trigger from an old one, you still havent left the first one
            --Without names you could enter one and then exit, thus falling
            --Still has glitches tho, as the enter hook often run before exit, making you fall at the transition
            --I will probably revert this to be a boolean, as I rather see that first glitch when more complex water systems is used
            if (exitEnt.inWater ~= nil and self:GetName() ~= nil and exitEnt.inWater == self:GetName()) then
                exitEnt.inWater = ""
            end
        end
    end
    
end

function Water:OnCreate()
    
    Trigger.OnCreate(self)
    
    self:SetPropagate(Actor.Propagate_Never)
    
    self:SetIsVisible(false)
    
end

Shared.LinkClassToMap("Water", Water.kMapName, {})<!--c2--></div><!--ec2-->

Player.lua
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->// ======= Copyright © 2003-2010, Unknown Worlds Entertainment, Inc. All rights reserved. =======
//
// lua\Player.lua
//
//    Created by:   Charlie Cleveland (charlie@unknownworlds.com)
//
// Player coordinates - z is forward, x is to the left, y is up.
// The origin of the player is at their feet.
//
// ========= For more information, visit us at http://www.unknownworlds.com =====================
Script.Load("lua/Globals.lua")
Script.Load("lua/TechData.lua")
Script.Load("lua/Utility.lua")
Script.Load("lua/LiveScriptActor.lua")
Script.Load("lua/PhysicsGroups.lua")

class 'Player' (LiveScriptActor)

if (Server) then
    Script.Load("lua/Player_Server.lua")
    Script.Load("lua/Bot_Player.lua")
else
    Script.Load("lua/Player_Client.lua")
    Script.Load("lua/Chat.lua")
end

Player.kMapName = "player"

Player.kModelName                   = PrecacheAsset("models/marine/male/male.model")
Player.kSpecialModelName            = PrecacheAsset("models/marine/male/male_special.model")
Player.kClientConnectSoundName      = PrecacheAsset("sound/ns2.fev/common/connect")
Player.kNotEnoughResourcesSound     = PrecacheAsset("sound/ns2.fev/marine/voiceovers/commander/more")
Player.kInvalidSound                = PrecacheAsset("sound/ns2.fev/common/invalid")
Player.kTooltipSound                = PrecacheAsset("sound/ns2.fev/common/tooltip")
Player.kChatSound                   = PrecacheAsset("sound/ns2.fev/common/chat")

// Animations
Player.kAnimRun = "run"
Player.kAnimTaunt = "taunt"
Player.kAnimStartJump = "jumpin"
Player.kAnimEndJump = "jumpout"
Player.kAnimJump = "jump"
Player.kAnimReload = "reload"
Player.kRunIdleSpeed = 1

Player.kLoginBreakingDistance = 150
Player.kUseRange  = 1.6
Player.kUseHolsterTime = .5
Player.kDefaultBuildTime = .2
    
Player.kGravity = -24
Player.kMass = 90.7 // ~200 pounds (incl. armor, weapons)
Player.kWalkBackwardSpeedScalar = 0.4
Player.kJumpHeight =  1  

// The physics shapes used for player collision have a "skin" that makes them appear to float, this makes the shape
// smaller so they don't appear to float anymore
Player.kSkinCompensation = 0.9
Player.kXZExtents = 0.35
Player.kYExtents = .95
Player.kViewOffsetHeight = Player.kYExtents * 2 - .28 // Eyes a bit below the top of the head. NS1 marine was 64" tall.
Player.kFov = 90
Player.kToolTipInterval = 18

// Percentage change in height when full crouched
Player.kCrouchShrinkAmount = .5
// Slow down players when crouching
Player.kCrouchSpeedScalar = .5
// How long does it take to crouch or uncrouch
Player.kCrouchAnimationTime = .25

Player.kMinVelocityForGravity = .5
Player.kThinkInterval = .2
Player.kMinimumPlayerVelocity = .05    // Minimum player velocity for network performance and ease of debugging

// Player speeds
Player.kWalkMaxSpeed = 5                // Four miles an hour = 6,437 meters/hour = 1.8 meters/second (increase for FPS tastes)
Player.kStartRunMaxSpeed = Player.kWalkMaxSpeed
Player.kRunMaxSpeed = 6.25              // 10 miles an hour = 16,093 meters/hour = 4.4 meters/second (increase for FPS tastes)
Player.kMaxWalkableNormal =  math.cos( math.rad(45) )

Player.kAcceleration = 50
Player.kRunAcceleration = 300
Player.kLadderAcceleration = 50

//Swim speeds - by feha
Player.kSwimForward = 1250 --Ridiculusly high because I added * input.time :P
Player.kSwimStrafe = 1250
Player.kSwimUp = 25

// Out of breath
Player.kTimeToLoseBreath = 10
Player.kTimeToGainBreath = 20

Player.kTauntMovementScalar = .05           // Players can only move a little while taunting

Player.kDamageIndicatorDrawTime = 1

Player.kMaxHotkeyGroups = 5

Player.kUnstickDistance = .1
Player.kUnstickOffsets = {
    Vector(0, Player.kUnstickDistance, 0),
    Vector(Player.kUnstickDistance, 0, 0),
    Vector(-Player.kUnstickDistance, 0, 0),
    Vector(0, 0, Player.kUnstickDistance),
    Vector(0, 0, -Player.kUnstickDistance)
}

// When changing these, make sure to update Player:CopyPlayerDataFrom. Any data which
// needs to survive between player class changes needs to go in here.
// Compensated variables are things that you want reverted when processing commands
// so basically things that are important for defining whether or not something can be shot
// for the player this is anything that can affect the hit boxes, like the animation that's playing,
// the current animation time, pose parameters, etc (not for the player firing but for the
// player being shot).
local networkVars =
{
    // Compensated means backed up and restored between calls to OnProcessMove, but only for other players, not yourself.
    viewYaw                 = "compensated interpolated angle",
    viewPitch               = "compensated interpolated angle",
    viewRoll                = "compensated interpolated angle",

    cameraDistance          = "float",
    desiredCameraDistance   = "float",
    thirdPerson             = "boolean",
    smoothCamera            = "boolean",
    
    // Controlling client index. -1 for not being controlled by a live player (ragdoll, fake player)
    clientIndex             = "integer",
    
    // In degrees
    fov                     = "integer (0 to 255)",
    
    velocity                = "compensated interpolated vector",
    gravityEnabled          = "boolean",
    
    // 0 means no active weapon, 1 means first child weapon, etc.
    activeWeaponIndex       = "integer (0 to 10)",
    activeWeaponHolstered   = "boolean",

    viewModelId             = "entityid",

    plasma                  = string.format("integer (0 to %d)", kMaxResources),
    teamCarbon              = string.format("integer (0 to %d)", kMaxResources),
    gameStarted             = "boolean",
    countingDown            = "boolean",
    frozen                  = "boolean",      
    
    timeOfDeath             = "float",
    timeOfLastUse           = "float",
  
    timeOfLastWeaponSwitch  = "float",
    crouching               = "compensated boolean",
    timeOfCrouchChange      = "compensated interpolated float",
    timeLegalCrouchTime     = "compensated interpolated float",
    
    flareStartTime          = "float",
    flareStopTime           = "float",
    flareScalar             = "float",
    
    desiredPitch            = "float",
    desiredRoll             = "float",

    showScoreboard          = "boolean",
    sayingsMenu             = "integer (0 to 6)",
    timeLastMenu            = "float",
    
    // True if target under reticle can be damaged
    reticleTarget           = "boolean",
    
    // Time we last did damage to a target
    timeTargetHit           = "float",
      
    // Set to true when jump key has been released after jump processed
    // Used to require the key to pressed multiple times
    jumpHandled             = "boolean",
    timeOfLastJump          = "float",
    onGround                = "boolean",
    onGroundNeedsUpdate     = "boolean",
    
    onLadder                = "boolean",
    
    //I want this to end up with prediction, prolyl isnt atm tho - by feha
    inWater                = string.format("string (%d)", kMaxEntityStringLength),
    
    // Player-specific mode. When set to kPlayerMode.Default, player moves and acts normally, otherwise
    // he doesn't take player input. Change mode and set modeTime to the game time that the mode
    // ends. ProcessEndMode() will be called when the mode ends. Return true from that to process
    // that mode change, otherwise it will go back to kPlayerMode.Default. Used for things like taunting,
    // building structures and other player actions that take time while the player is stationary.
    mode                    = "enum kPlayerMode",
    
    // Time when mode will end. Set to -1 to have it never end.
    modeTime                = "float",
    
    primaryAttackLastFrame      = "boolean",
    secondaryAttackLastFrame    = "boolean",
    // Indicates how active the player has been
    outOfBreath             = "integer (0 to 255)",
    
    baseYaw                 = "float",
    basePitch               = "float",
    baseRoll                = "float",
    
    // The next point in the world to go to in order to reach an order target location
    nextOrderWaypoint       = "vector",
    
    // The final point in the world to go to in order to reach an order target location
    finalWaypoint           = "vector",
    
    // Whether this entity has a next order waypoint
    nextOrderWaypointActive = "boolean",
    
    // Move, Build, etc.
    waypointType            = "enum kTechId",
    
    fallReadyForPlay        = "integer (0 to 3)",

}

function Player:OnCreate()
    
    LiveScriptActor.OnCreate(self)
    
    self:SetLagCompensated(true)
    
    self:SetUpdates(true)
    
    // Create the controller for doing collision detection.
    // Just use default values for the capsule size for now. Player will update to correct
    // values when they are known.
    self:CreateController(PhysicsGroup.PlayerControllersGroup, 1, 0.5)
        
    self.viewYaw        = 0
    self.viewPitch      = 0
    self.viewRoll       = 0
    self.maxExtents     = Vector( LookupTechData(self:GetTechId(), kTechDataMaxExtents, Vector(Player.kXZExtents, Player.kYExtents, Player.kXZExtents)) )
    self.viewOffset     = Vector()

    self.desiredCameraDistance = 0
    self.thirdPerson = false
    self.smoothCamera = false
    self.clientIndex = -1
    self.client = nil

    self.cameraDistance = 0
    
    self.velocity = Vector(0, 0, 0)
    self.gravityEnabled = true
    self.activeWeaponIndex = 0
    self.activeWeaponHolstered = false
  
    self.overlayAnimationName = ""
  
    self.showScoreboard = false
    
    if Server then
        self.sendTechTreeBase = false
    end
    
    if Client then
        self.showSayings = false
    end
    
    self.sayingsMenu = 0
    self.timeLastMenu = 0    
    self.timeLastSayingsAction = 0
    self.reticleTarget = false
    self.timeTargetHit = 0
    self.score = 0
    self.kills = 0
    self.deaths = 0
    self.displayedTooltips = {}
    
    self.sighted = false
    self.jumpHandled = false
    self.leftFoot = true
    self.mode = kPlayerMode.Default
    self.modeTime = -1
    self.primaryAttackLastFrame = false
    self.secondaryAttackLastFrame = false
    self.outOfBreath = 0
    
    self.baseYaw = 0
    self.basePitch = 0
    self.baseRoll = 0
    
    self.requestsScores = false  
    self.viewModelId = Entity.invalidId
    
    self.usingStructure = nil
    self.timeOfLastUse  = 0
    self.timeOfLastWeaponSwitch = nil
    self.respawnQueueEntryTime = nil

    self.timeOfDeath = nil
    self.crouching = false
    self.timeOfCrouchChange = 0
    self.timeLegalCrouchTime = 0
    self.onGroundNeedsUpdate = true
    self.onGround = false
    
    self.onLadder = false
    
    self.timeLastOnGround = 0
    
    self.fallReadyForPlay = 0

    self.flareStartTime = 0
    self.flareStopTime = 0
    self.flareScalar = 1
    self.plasma = 0
        
    // Make the player kinematic so that bullets and other things collide with it.
    self:SetPhysicsGroup(PhysicsGroup.PlayerGroup)
    
    self.nextOrderWaypoint = nil
    self.finalWaypoint = nil
    self.nextOrderWaypointActive = false
    self.waypointType = kTechId.None
    
end

function Player:OnInit()
    
    LiveScriptActor.OnInit(self)
    
    if Server then
          
        self:InitWeapons()
        
    end

    // Set true on creation
    if Server then
        self:SetName(kDefaultPlayerName)
    end
    self:SetScoreboardChanged(true)
    
    self:SetViewOffsetHeight(self:GetMaxViewOffsetHeight())
    self:SetFov(self:GetStartFov())
    
    self:SetFov(self:GetStartFov())
    
    self:UpdateControllerFromEntity()
        
    self:TriggerEffects("idle")
        
    if Server then
        self:SetNextThink(Player.kThinkInterval)
    end
    
    // Initialize hotkey groups. This is in player because
    // it needs to be preserved across player replacements.
    
    // Table of table of ids, in order of hotkey groups
    self:InitializeHotkeyGroups()
    
    self:LoadHeightmap()
    
end

function Player:InitializeHotkeyGroups()

    self.hotkeyGroups = {}
    
    for i = 1, Player.kMaxHotkeyGroups do
        table.insert(self.hotkeyGroups, {})
    end

end

function Player:OnEntityChange(oldEntityId, newEntityId)

    if Server then
    
        // Loop through hotgroups and update accordingly
        for i = 1, Player.kMaxHotkeyGroups do
        
            for index, entityId in ipairs(self.hotkeyGroups[i]) do
            
                if(entityId == oldEntityId) then
                
                    if(newEntityId ~= nil) then
                    
                        self.hotkeyGroups[i][index] = newEntityId
                        
                    else
                    
                        table.remove(self.hotkeyGroups[i], index)
                        
                    end
                    
                    if self.SendHotkeyGroup ~= nil then
                        self:SendHotkeyGroup(i)
                    end
                    
                end
                
            end
            
       end
  
   end
  
end

function Player:GetStatusDescription()
    return string.format("%s - %s", self:GetName(), self:GetClassName()), nil
end

function Player:GetHealthDescription()
    return "Health", self:GetHealth() / self:GetMaxHealth()
end

// Special unique client-identifier
function Player:GetClientIndex()
    return self.clientIndex
end

/**
* Sets the view angles for the player. Note that setting the yaw of the
* view will also adjust the player's yaw. Pass true for second parameter
* to indicate that this is from player input.
*/
function Player:SetViewAngles(viewAngles, playerInput)

    self.viewYaw = viewAngles.yaw + self.baseYaw
    self.viewPitch = viewAngles.pitch + self.basePitch
    self.viewRoll  = viewAngles.roll + self.baseRoll

    local angles = Angles(self:GetAngles())
    angles.yaw  = self.viewYaw

    self:SetAngles(angles)

end

function Player:SetOffsetAngles(offsetAngles)

    self:SetBaseViewAngles(offsetAngles)      
    self:SetViewAngles(Angles(0, 0, 0))
    self:SetAngles(offsetAngles)

    if Server then        
        Server.SendNetworkMessage(self, "ResetMouse", {}, true)
    else
        Client.SetPitch(0)
        Client.SetYaw(0)
    end

end

/**
* Gets the view angles for the player.
*/
function Player:GetViewAngles()
    return Angles(self.viewPitch, self.viewYaw, self.viewRoll)
end

function Player:GetViewAnglesCoords()

    local currentCoords = self:GetViewAngles():GetCoords()
    VectorCopy(self:GetOrigin(), currentCoords.origin)
    currentCoords.origin = currentCoords.origin + self:GetViewOffset()
    
    return currentCoords

end

function Player:SetBaseViewAngles(viewAngles)

    self.baseYaw = viewAngles.yaw
    self.basePitch = viewAngles.pitch
    self.baseRoll = viewAngles.roll

end

/**
* Whenever view angles are needed this function must be called
* to compute them.
*/
function Player:ConvertToViewAngles(forPitch, forYaw, forRoll)

    return Angles(forPitch + self.basePitch, forYaw + self.baseYaw, forRoll + self.baseRoll)

end

function Player:OverrideInput(input)

    // Invert mouse if specified in options
    local invertMouse = Client.GetOptionBoolean ( kInvertedMouseOptionsKey, false )
    if invertMouse then
        input.pitch = -input.pitch
    end
    
    local maxPitch = Math.Radians(89.9)
    input.pitch = Math.Clamp(input.pitch, -maxPitch, maxPitch)
    
    if self.timeClosedMenu and (Shared.GetTime() < self.timeClosedMenu + .25) then
    
        // Don't allow weapon firing
        local removePrimaryAttackMask = bit.bxor(0xFFFFFFFF, Move.PrimaryAttack)
        input.commands = bit.band(input.commands, removePrimaryAttackMask)
        
    end
    
    self:OverrideSayingsMenu(input)
    
end

function Player:OverrideSayingsMenu(input)

    if(self:GetHasSayings() and (bit.band(input.commands, Move.ToggleSayings1) ~= 0 or bit.band(input.commands, Move.ToggleSayings2) ~= 0)) then
    
        // If enough time has passed
        if(self.timeLastSayingsAction == nil or (Shared.GetTime() > self.timeLastSayingsAction + .2)) then

            local newMenu = ConditionalValue(bit.band(input.commands, Move.ToggleSayings1) ~= 0, 1, 2)

            // If not visible, bring up menu
            if(not self.showSayings) then
            
                self.showSayings = true
                self.showSayingsMenu = newMenu
                
            // else if same menu and visible, hide it
            elseif(newMenu == self.showSayingsMenu) then
            
                self.showSayings = false
                self.showSayingsMenu = nil                
            
            // If different, change menu without showing or hiding
            elseif(newMenu ~= self.showSayingsMenu) then
            
                self.showSayingsMenu = newMenu
                
            end
            
        end
        
        // Sayings toggles are handled client side.
        local removeToggleSayingsMask = bit.bxor(0xFFFFFFFF, Move.ToggleSayings1)
        input.commands = bit.band(input.commands, removeToggleSayingsMask)
        removeToggleSayingsMask = bit.bxor(0xFFFFFFFF, Move.ToggleSayings2)
        input.commands = bit.band(input.commands, removeToggleSayingsMask)

        // Record time
        self.timeLastSayingsAction = Shared.GetTime()
        
    end
    
    // Intercept any execute sayings commands.
    if self.showSayings then
        local weaponSwitchCommands = { Move.Weapon1, Move.Weapon2, Move.Weapon3, Move.Weapon4, Move.Weapon5 }
        for i, weaponSwitchCommand in ipairs(weaponSwitchCommands) do
            if bit.band(input.commands, weaponSwitchCommand) ~= 0 then
                // Tell the server to execute this saying.
                local message = BuildExecuteSayingMessage(i, self.showSayingsMenu)
                Client.SendNetworkMessage("ExecuteSaying", message, true)
                local removeWeaponMask = bit.bxor(0xFFFFFFFF, weaponSwitchCommand)
                input.commands = bit.band(input.commands, removeWeaponMask)
                self.showSayings = false
            end
        end
    end

end

// Returns current FOV
function Player:GetFov()
    return self.fov
end

function Player:SetFov(fov)
    self.fov = fov
end

function Player:SetGravityEnabled(state)
    self.gravityEnabled = state
end

// Initial FOV when spawning as class. Can change through console
// commands, weapons, etc. but this is the base.
function Player:GetStartFov()
    return Player.kFov
end

function Player:SetDesiredCameraDistance(distance)
    self.desiredCameraDistance = math.max(distance, 0)
    self.thirdPerson = ((self.desiredCameraDistance > 0) or (self.cameraDistance > 0))
end

function Player:UpdateCamera(timePassed)
    
    if(self.cameraDistance ~= self.desiredCameraDistance) then
    
        local diff = (self.desiredCameraDistance - self.cameraDistance)
        local change = ConditionalValue(GetSign(diff) > 0, 10 * timePassed, -16 * timePassed)
        
        local newCameraDistance = self.cameraDistance + change
        
        if(math.abs(diff) < math.abs(change)) then
            newCameraDistance = self.desiredCameraDistance
        end

        self:SetCameraDistance(newCameraDistance)
        
    end
    
end

function Player:SetCameraDistance(distance)
    self.cameraDistance = math.max(distance, 0)
    self.thirdPerson = ((self.desiredCameraDistance > 0) or (self.cameraDistance > 0))
end

function Player:GetIsThirdPerson()
    return self.thirdPerson
end

// Set to 0 to get out of third person
function Player:SetIsThirdPerson(distance)
    self:SetDesiredCameraDistance(distance)
end

function Player:GetIsFirstPerson()
    return (Client and (Client.GetLocalPlayer() == self) and not self:GetIsThirdPerson())
end

function Player:GetCameraDistance()
    return self.cameraDistance
end

// Also specifies listener position
function Player:GetViewOffset()
    return self.viewOffset
end

// Stores the player's current view offset. Calculated from GetMaxViewOffset() and crouch state.
function Player:SetViewOffsetHeight(newViewOffsetHeight)

    VectorCopy(Vector(0, newViewOffsetHeight, 0), self.viewOffset)
    
end

function Player:GetEyePos()
    return self:GetOrigin() + self.viewOffset    
end

function Player:GetMaxViewOffsetHeight()
    return Player.kViewOffsetHeight
end

function Player:GetCanViewModelIdle()
    return self:GetIsAlive() and self:GetCanNewActivityStart() and (self.mode == kPlayerMode.Default)
end

function Player:LoadHeightmap()

    // Load height map
    self.heightmap = HeightMap()  
    local heightmapFilename = string.format("maps/overviews/%s.hmp", Shared.GetMapName())
    
    if(not self.heightmap:Load(heightmapFilename)) then
        Shared.Message("Couldn't load height map " .. heightmapFilename)
        self.heightmap = nil
    end

end

function Player:GetHeightmap()
    return self.heightmap
end

// worldX => -map y
// worldZ => +map x
function Player:GetMapXY(worldX, worldZ)

    local success = false
    local mapX = 0
    local mapY = 0

    if self.heightmap then
        mapX = self.heightmap:GetMapX(worldZ)
        mapY = self.heightmap:GetMapY(worldX)
    else
        --Print("Player:GetMapXY(): heightmap is nil")
        return false, 0, 0
    end

    if mapX >= 0 and mapX <= 1 and mapY >= 0 and mapY <= 1 then
        success = true
    end

    return success, mapX, mapY

end

// Plays view model animation, given a string or a table of weighted entries.
// Returns length of animation or 0 if animation wasn't found.
function Player:SetViewAnimation(animName, noForce, blend, speed)

    local length = 0.0
    
    if not speed then
        speed = 1
    end
    
    if (animName ~= nil and animName ~= "") then
    
        local viewModel = self:GetViewModelEntity()
        if (viewModel ~= nil) then

            local force = ConditionalValue(noForce, false, true)
            local success = false
            
            if blend then
                success = viewModel:SetAnimationWithBlending(animName, self:GetBlendTime(), force, speed)
                length = viewModel:GetAnimationLength(animName) / speed                
            else
                success = viewModel:SetAnimation(animName, force, speed)
                length = viewModel:GetAnimationLength(animName) / speed
            end
            
            if success then
            
                if Client then
                    self:UpdateRenderModel()
                end
                
                viewModel:UpdateBoneCoords()
            end
            
            if not success and force then
                Print("%s:SetViewAnimation(%s) failed.", self:GetClassName(), tostring(animSpecifier))
            end
            
        else
            Print("Player:SetViewAnimation(%s) - couldn't find view model", animName)
        end
        
    end
    
    return length
    
end

function Player:GetViewAnimationLength(animName)

    local length = 0
    
    local viewModel = self:GetViewModelEntity()
    if (viewModel ~= nil) then
        if animName and animName ~= "" then
            length = viewModel:GetAnimationLength(animName)
        else
            length = viewModel:GetAnimationLength(nil)
        end
    end
    
    return length
    
end

function Player:SetViewOverlayAnimation(overlayAnim)

    local viewModel = self:GetViewModelEntity()
    if (viewModel ~= nil) then
        viewModel:SetOverlayAnimation(overlayAnim)
    end
    
end

function Player:GetVelocity()
    return self.velocity
end

function Player:SetVelocity(velocity)

    VectorCopy(velocity, self.velocity)

    // Snap to 0 when close to zero for network performance and our own sanity
    if (math.abs(self.velocity:GetLength()) < Player.kMinimumPlayerVelocity) then
    
        self.velocity:Scale(0)
        
    end
    
end

function Player:GetController()

    return self.controller
    
end

function Player:PrimaryAttack()

    local weapon = self:GetActiveWeapon()    
    if weapon and self:GetCanNewActivityStart() then
        weapon:OnPrimaryAttack(self)
    end
    
end

function Player:SecondaryAttack()

    local weapon = self:GetActiveWeapon()        
    if weapon and self:GetCanNewActivityStart() then
        weapon:OnSecondaryAttack(self)
    end

end

function Player:PrimaryAttackEnd()

    local weapon = self:GetActiveWeapon()
    if weapon then
        weapon:OnPrimaryAttackEnd(self)
    end

end

function Player:SecondaryAttackEnd()

    local weapon = self:GetActiveWeapon()
    if weapon then
        weapon:OnSecondaryAttackEnd(self)
    end
    
end

function Player:SelectNextWeapon()

    self:SelectWeaponWithFinder(self.FindChildEntity)
    
end

function Player:SelectPrevWeapon()

    self:SelectWeaponWithFinder(self.FindChildEntityReverse)
    
end

function Player:SelectWeaponWithFinder(finderFunction)
    
    local entity = finderFunction(self, self:GetActiveWeapon())
    
    if(entity == nil) then
        entity = finderFunction(self, nil)
    end
    
    while(entity ~= nil and not entity:isa("Weapon")) do
        entity = finderFunction(self, entity)
    end

    if(entity ~= nil and self:GetCanNewActivityStart()) then
        self:SetActiveWeapon(entity:GetMapName())
    end
    
end

function Player:GetActiveWeapon()

    local activeWeapon = nil
    
    if(self.activeWeaponIndex ~= 0) then
    
        local weapons = self:GetHUDOrderedWeaponList()
        
        if self.activeWeaponIndex <= table.count(weapons) then
            activeWeapon = weapons[self.activeWeaponIndex]
        end
        
    end
    
    return activeWeapon
    
end

function Player:GetActiveWeaponName()

    local activeWeaponName = ""
    local activeWeapon = self:GetActiveWeapon()
    
    if activeWeapon ~= nil then
        activeWeaponName = activeWeapon:GetClassName()
    end
    
    return activeWeaponName
    
end

function Player:Reload()
    local weapon = self:GetActiveWeapon()
    if(weapon ~= nil and self:GetCanNewActivityStart()) then
        weapon:OnReload(self)
    end
end

/**
* Check to see if there's a LiveScriptActor we can use. Checks any attachpoints returned from  
* GetAttachPointOrigin() and if that fails, does a regular traceray. Returns true if we processed the action.
*/
function Player:Use()

    local success = false
    
    local startPoint = self:GetViewOffset() + self:GetOrigin()
    local viewCoords = self:GetViewAngles():GetCoords()
    
    local elapsedTime = 0
    if self.timeOfLastUse ~= 0 then
        elapsedTime = math.min(Shared.GetTime() - self.timeOfLastUse, Player.kDefaultBuildTime)
    end
    
    // Get entities in radius
    
    local ents = GetEntitiesIsaInRadius("LiveScriptActor", self:GetTeamNumber(), self:GetOrigin(), Player.kUseRange)
    for index, entity in ipairs(ents) do
    
        // Look for attach point
        local attachPointName = entity:GetUseAttachPoint()
        
        if attachPointName ~= "" and entity:GetCanBeUsed(self) then

            local attachPoint = entity:GetAttachPointOrigin(attachPointName)
            local toAttachPoint = attachPoint - startPoint
            local legalUse = toAttachPoint:GetLength() < Player.kUseRange and viewCoords.zAxis:DotProduct(GetNormalizedVector(toAttachPoint)) > .8
            
            if(legalUse and entity:OnUse(self, elapsedTime, true, attachPoint)) then
            
                success = true
                
                break
                
            end
            
        end
        
    end
    
    // If failed, do a regular trace with entities that don't have use attach points
    if not success then

        local endPoint = startPoint + viewCoords.zAxis * Player.kUseRange
        local trace = Shared.TraceRay(startPoint, endPoint, PhysicsMask.AllButPCs, EntityFilterOne(self))

Comments

  • l3lessedl3lessed Join Date: 2010-06-07 Member: 71977Members
    Keep up the wonderful work. I was sad to here max say they had no plans to add water. This is adds a lot when it comes to dynamic visual environmental and dynamic combat environments. Hopefully someone with the know how can help you out and get some good looking water going.
  • FehaFeha Join Date: 2006-11-16 Member: 58633Members
    edited February 2011
    Woot, I finally got feedback! XD

    I kinda stopped developing water after posting this as it seemed like noone was interested at all, making it seem like quite much work for an 'unwanted' feature :P.

    I am not sure if I should stick with triggers and wait for them to get fixed, or do that but change to find the players bounding box and check for intersection between it and the trigger.
    The alternative is to actually make my own trigger entity, but that feels like unnecesary work :S...

    EDIT:
    Maybe I could try to not check if collided entity is a player, but instead create some sort of bounding box entity which I stick to player and then let the water check if collided entity is that.
  • ThaldarinThaldarin Alonzi&#33; Join Date: 2003-07-15 Member: 18173Members, Constellation
    I like the idea of this coming along. A temporary solution to the liquid problem for mappers.

    I'm not big to mess around with this stuff as I don't know what I'm doing. Any chance of a preview video showing what's going on your side with this effect?

    You guys in here keep on amazing me with all your LUA scripting =]
  • FehaFeha Join Date: 2006-11-16 Member: 58633Members
    I have yet to grasp the concept of making youtube videos, sorry :P.

    If you wanna know how it looks, go into the map eedito and create a team_join. The physical representation in editor looks the same, as they both use the same base entity (trigger, not sure if you can spawn it in editor tho).

    Ingame you cant see the water, I have not made any graphics whatsoever. If you walk into water the gravity is lowered, and totally removed when you try to walk (so you can go upwards xD), and if you hold space you start float upwards. The swim model (not graphical model, control model, just like lerks have a flight model xD) is designed to be a lot like how its in hl2, as the description might make you understand XD.

    Looking in thirdperson is hilarious, as the marine is walking midair XD
  • ThaldarinThaldarin Alonzi&#33; Join Date: 2003-07-15 Member: 18173Members, Constellation
    It's makeshift for now, but maybe talking to tig in this thread: <a href="http://www.unknownworlds.com/forums/index.php?showtopic=111726" target="_blank">http://www.unknownworlds.com/forums/index....howtopic=111726</a> - He's using some particle effects to make a temporary water effect. I'd suggest asking him for his cinematic so you can add it to your file for testing purpose .
  • FehaFeha Join Date: 2006-11-16 Member: 58633Members
    edited February 2011
    I will probably end up with applying textures to a box model, if I just can figure out how ;).

    EDIT:
    Also, hos water in that map looks awesome! Particles light that can simulate dynamic water XD.
    I think that I will end up letting mappers set the water to be invisible, so that it can be used together with awesome particle fx XD.
  • devicenulldevicenull Join Date: 2003-04-30 Member: 15967Members, NS2 Playtester, Squad Five Blue
    I'd really suggest you use something like github.com for this.. Posting the source code like this means your modifications are mostly useless once the next patch comes around. People would have to compare the old version of the code to what you posted to figure out what changed, then port everything to the new version... Source control just makes this 100x easier.
  • FehaFeha Join Date: 2006-11-16 Member: 58633Members
    I am more used to svn, so if that can do the same it would be better to me.

    Also, I figure that as 1 file is completely custom, another is simply appending a few lines at end, and a third is 1 line aslong as it is below a certain point, the only big work I need to do is in player.lua
    And because I added a comment to find all my code changes, it wont be really hard to put them back at the right places anyway XD.

    Still, you are right there is better ways to do it, and I will look into it later. Now I must sleep tho ;P.
Sign In or Register to comment.