An analysis of the game lua code

2

Comments

  • hyperrifthyperrift Join Date: 2010-12-16 Member: 75714Members
    Under no circumstances should the burn duration of the flamethrower be random. Introducing a random element with such a wide range of outcomes can cause drastically divergent gameplay.

    There is no reason for it to not be a set duration. Make it five second or ten seconds or whatever, but make it static.
  • extolloextollo Ping Blip Join Date: 2010-07-16 Member: 72457Members
    if they really want the it to be based on a percent per sec they could set the per trial probability so that it equals out to the intent of kStopFireProbability.

    stopFireChance = 1 - (1 - kStopFireProbability) ^ (PlayingTeam.kUpdateGameEffectsInterval)

    i think i got this right. ie. n trials for a sec would equal the kStopFireProbability
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    edited January 2011
    @hyperrift: that's definitely the classic (and reliable) way of doing things.

    One thing to consider though is that gorge spray is supposed to put out flames.
    <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->//SpitSpray.lua
    function SpitSpray:HealEntities(player)
                    if isHealPlayer then
                        // Put out entities on fire
                        targetEntity:SetGameEffectMask(kGameEffect.OnFire, false)<!--c2--></div><!--ec2-->

    <!--quoteo(post=1823337:date=Jan 12 2011, 03:08 AM:name=jbaker8935)--><div class='quotetop'>QUOTE (jbaker8935 @ Jan 12 2011, 03:08 AM) <a href="index.php?act=findpost&pid=1823337"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->if they really want the it to be based on a percent per sec they could set the per trial probability so that it equals out to the intent of kStopFireProbability.

    stopFireChance = 1 - (1 - kStopFireProbability) ^ (PlayingTeam.kUpdateGameEffectsInterval)

    i think i got this right. ie. n trials for a sec would equal the kStopFireProbability<!--QuoteEnd--></div><!--QuoteEEnd-->
    Yeah that works out right.
    = 0.047586204
    Doing the linear interpolation between 0.9 and 1.2 seconds (trials 3 and 4) for 1 second, yields a chance very close to 0.15.
    But that still yields a static stopFireChance, which still leads to a non-linear cumulative chance... So at 3 seconds, for example, it isn't a 45% chance, it's a 38.6% chance.
    But if that's okay, then that's okay.
    <img src="http://img841.imageshack.us/img841/5475/ns2burningputoutchance2.png" border="0" class="linked-image" />
  • DeadzoneDeadzone Join Date: 2003-07-03 Member: 17911Members, Reinforced - Shadow
    <!--quoteo(post=1823335:date=Jan 11 2011, 02:43 PM:name=hyperrift)--><div class='quotetop'>QUOTE (hyperrift @ Jan 11 2011, 02:43 PM) <a href="index.php?act=findpost&pid=1823335"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->Under no circumstances should the burn duration of the flamethrower be random. Introducing a random element with such a wide range of outcomes can cause drastically divergent gameplay.

    There is no reason for it to not be a set duration. Make it five second or ten seconds or whatever, but make it static.<!--QuoteEnd--></div><!--QuoteEEnd-->

    I have to agree on this. I'm a big TF2 fan, and the pyro's flamethrower will keep a victim burning for the same amount of time. (or similar at least - I've never timed it, but it's never gone on and on forever or ended really fast) A player on fire generally can tell if they're in danger of dying from the burn or not. If they're not in danger, they can continue play and not worry about feeling cheated that they just kept burning. If they know they're in trouble, it adds a fun layer of urgency trying to find a method to put the fire out. (water, health pack, medic, a friendly airblast, jarate, whatever)

    Let the player know what to expect, don't randomly pull the rug out from under them. It's not fun, and you can't "learn" a good consistent response fire except to panic and hope you don't die this time.
  • McGlaspieMcGlaspie www.team156.com Join Date: 2010-07-26 Member: 73044Members, Super Administrators, Forum Admins, NS2 Developer, NS2 Playtester, Squad Five Blue, Squad Five Silver, Squad Five Gold, Reinforced - Onos, WC 2013 - Gold, Subnautica Playtester
    edited January 2011
    After glancing at the source code, the following comment just makes me cringe:

    "// See if we catch anyone else on fire"

    Even with correcting the burn time issue, having things catch other things on fire would be insane. FT would completely cancel out Hydras and make it dangerous for alien players to "run back to base to heal". I hope I'm just being pessimistic.
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    It's always been a feature under consideration. But that's why they've put it under comments. If they decide to try it, they just need to remove the commenting.
    There are a number of other features that are 'done', but disabled like this (as well as features that are planned but incomplete).
  • extolloextollo Ping Blip Join Date: 2010-07-16 Member: 72457Members
    edited January 2011
    <!--quoteo(post=1823338:date=Jan 11 2011, 02:08 PM:name=Harimau)--><div class='quotetop'>QUOTE (Harimau @ Jan 11 2011, 02:08 PM) <a href="index.php?act=findpost&pid=1823338"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->Yeah that works out right.
    = 0.047586204
    Doing the linear interpolation between 0.9 and 1.2 seconds (trials 3 and 4) for 1 second, yields a chance very close to 0.15.
    But that still yields a static stopFireChance, which still leads to a non-linear cumulative chance... So at 3 seconds, for example, it isn't a 45% chance, it's a 38.6% chance.
    But if that's okay, then that's okay.<!--QuoteEnd--></div><!--QuoteEEnd-->

    right. it only helps making the tuning more accurate (ie you could change the effects interval without impacting outcome). the usual cumulative probability formula applies.
  • Slickk-Slickk- Join Date: 2007-11-26 Member: 63019Members
    <!--quoteo(post=1823278:date=Jan 11 2011, 07:10 AM:name=Harimau)--><div class='quotetop'>QUOTE (Harimau @ Jan 11 2011, 07:10 AM) <a href="index.php?act=findpost&pid=1823278"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->Well, each hydra does do the following actions each think.
    <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->OnThink
    -> AcquireTarget
      -> GetSortedTargetList
      -> GetTargetValid
    -> AttackTarget
      -> CreateSpikeProjectile
        -> [...]
    -> OR Alert
      -> GetIsEnemyNearby
    -> SetNextThink<!--c2--></div><!--ec2-->
    GetSortedTargetList seems the most intensive, but the origin and the range are put into the function from the beginning, so it shouldn't be scanning everything on the map.
    There are a few instances (33, so relatively few) in the code of "GetGamerules():GetEntities(arguments)", but I can't figure out how they work as I can't find the function "GetEntities(arguments)". The closest is "GetEntitiesIsA(arguments)", and I wonder why the hydra doesn't use the function "GetEntitiesIsAInRadius(arguments)", as the arguments match:
    <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->//Entity.lua
    function GetEntitiesIsaInRadius(className, teamNumber, origin, radius, checkXZOnly, visibleOnly, log)

    //Hydra_Server.lua
    function Hydra:GetSortedTargetList()
        local targets = GetGamerules():GetEntities("LiveScriptActor", GetEnemyTeamNumber(self:GetTeamNumber()), hydraAttackOrigin, Hydra.kRange)<!--c2--></div><!--ec2-->
    It's the same case for sentries, as well.
    Could this be the source of the problem, or are my searching skills not enough? I figure if it weren't working, hydras and sentries wouldn't work at all though, so I don't know... :/ Unless it's managing to scan every target on the map regardless...<!--QuoteEnd--></div><!--QuoteEEnd-->

    I just had another look at this, I see what you mean, the function doesn't exist as far as i can see.
    I checked all the logical lua files and i can't find it. I went to GetGamerules() and then followed that to Entity which then was referring to GetEntitiesIsa("...").
    The variables are slightly different as well as if they are meant to be passing the intended thing but theres nothing linking them together.

    Totally agree though, if it was broken i wouldn't be having my ass handed to me when i walk around corners in game.

    Would be interested what the team say..


    <!--quoteo(post=1823352:date=Jan 11 2011, 07:58 PM:name=Harimau)--><div class='quotetop'>QUOTE (Harimau @ Jan 11 2011, 07:58 PM) <a href="index.php?act=findpost&pid=1823352"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->It's always been a feature under consideration. But that's why they've put it under comments. If they decide to try it, they just need to remove the commenting.
    There are a number of other features that are 'done', but disabled like this (as well as features that are planned but incomplete).<!--QuoteEnd--></div><!--QuoteEEnd-->

    I think that's the beauty of adding new features like this, you can tinker or totally remove them. It's not like NS1 failed without a FT.
  • MOOtantMOOtant Join Date: 2010-06-25 Member: 72158Members
    Less tables more graphs, please.
  • aeroripperaeroripper Join Date: 2005-02-25 Member: 42471NS1 Playtester, Forum Moderators, Constellation
    edited January 2011
    I hope its like it is in NS1 now, 1 parasite and 2 bites = marine death at armor 0. Nice work Harimau, you the man.
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    edited January 2011
    Thanks, man. :P

    <!--quoteo(post=1823374:date=Jan 12 2011, 05:28 AM:name=jbaker8935)--><div class='quotetop'>QUOTE (jbaker8935 @ Jan 12 2011, 05:28 AM) <a href="index.php?act=findpost&pid=1823374"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->right. it only helps making the tuning more accurate (ie you could change the effects interval without impacting outcome). the usual cumulative probability formula applies.<!--QuoteEnd--></div><!--QuoteEEnd-->
    Yeah, that makes sense. It's a good approach.

    <!--quoteo(post=1823406:date=Jan 12 2011, 08:05 AM:name=Slickk-)--><div class='quotetop'>QUOTE (Slickk- @ Jan 12 2011, 08:05 AM) <a href="index.php?act=findpost&pid=1823406"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->I just had another look at this, I see what you mean, the function doesn't exist as far as i can see.
    I checked all the logical lua files and i can't find it. I went to GetGamerules() and then followed that to Entity which then was referring to GetEntitiesIsa("...").
    The variables are slightly different as well as if they are meant to be passing the intended thing but theres nothing linking them together.

    Totally agree though, if it was broken i wouldn't be having my ass handed to me when i walk around corners in game.

    Would be interested what the team say..<!--QuoteEnd--></div><!--QuoteEEnd-->
    I'd definitely like to get a comment on this.

    <!--quoteo(post=1823415:date=Jan 12 2011, 09:06 AM:name=MOOtant)--><div class='quotetop'>QUOTE (MOOtant @ Jan 12 2011, 09:06 AM) <a href="index.php?act=findpost&pid=1823415"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->Less tables more graphs, please.<!--QuoteEnd--></div><!--QuoteEEnd-->
    Tables are better for raw values, graphs are good for looking at comparisons, and local and global trends. I could always label relevant data points though. I'll definitely consider it.
  • MaxMax Technical Director, Unknown Worlds Entertainment Join Date: 2002-03-15 Member: 318Super Administrators, Retired Developer, NS1 Playtester, Forum Moderators, NS2 Developer, Constellation, Subnautica Developer, Pistachionauts, Future Perfect Developer
    GetEntities exists, it's in NS2GameRules.lua
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    Ahh, I see how it works. Shared.lua loads NS2Gamerules.lua; GetGameRules() in Gamerules_Global.lua checks for the installed gamerules, or else uses the default: Gamerules.lua. So whenever it uses GetGameRules():GetEntities(), it's using the GetEntities function from NS2Gamerules.lua.
  • KoruyoKoruyo AUT Join Date: 2009-06-06 Member: 67724Members, Reinforced - Shadow
    edited January 2011
    I got a job for you Harimau,

    You cant spent res if you only got the exact amount to buy the thing you wish... you have to wait until you get +1 res.

    Example, I got 5 res and want to upgrade a Res Tower... => Not possible until i got at least 6 res. even tho it only costs 5res... I dont think thats the way it should be...
  • ZekZek Join Date: 2002-11-10 Member: 7962Members, NS1 Playtester, Constellation, Reinforced - Shadow
    I never liked the parasite + 2 bites mechanic in NS1. It was a quick fix to early game balance problems that unfairly punished newer players that didn't know the trick. IMO parasite shouldn't be a combat tool, it should only be used for its actual purpose: tracking marines.
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    edited January 2011
    Koruyo:
    Retracted.
    I had a theory, realised it was wrong as the code should actually be working with those operators. So my new theory is that it's displaying the wrong amount of carbon available. Haven't checked this, but it seems most likely.

    See:
    <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->//Commander_Server.lua
        // Handle tech tree actions that cost carbon    
        if(techNode:GetIsResearch() or techNode:GetIsUpgrade() or techNode:GetIsBuild() or techNode:GetIsEnergyBuild()) then
            local costsEnergy = techNode:GetIsEnergyBuild()
            local teamCarbon = team:GetCarbon()
            local energy = entity:GetEnergy()
            if (not costsEnergy and cost <= teamCarbon) or (costsEnergy and cost <= energy) then
                    [...]
            else
                self:TriggerNotEnoughResourcesAlert()
            end

        // Handle plasma-based abilities
        elseif(techNode:GetIsAction() or techNode:GetIsBuy()) then
            local playerPlasma = self:GetPlasma()
            if(cost == nil or cost <= playerPlasma) then
                    [...]
            else
                self:TriggerNotEnoughResourcesAlert()
            end
    [...]
    end<!--c2--></div><!--ec2-->
    Personally I would have used teamCarbon >= cost, and playerPlasma >= cost, as it's more intuitive, but it shouldn't make a difference, I don't think.

    I'm still stumped as to how <b>different things</b> are specified as costing carbon, energy or plasma. I've been searching but I can't find it. I've been searching mostly in regards to the 20 <b>Carbon</b> cost of the Sentry - I know that not everything costs Carbon as commander (which was one theory as to how it might be broken, if it is indeed broken), because the energy costs of MACs still works as it should.
    So far I can only figure out resource requirements, and subtracting from resources (given a resource type, or action type). The actual resource type (e.g. Carbon) or action type (e.g. GetIsBuild()) for an item still eludes me.

    Is "nil" the same as "0" in lua, or are they different concepts?

    <!--quoteo(post=1823867:date=Jan 13 2011, 02:44 PM:name=Zek)--><div class='quotetop'>QUOTE (Zek @ Jan 13 2011, 02:44 PM) <a href="index.php?act=findpost&pid=1823867"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->I never liked the parasite + 2 bites mechanic in NS1. It was a quick fix to early game balance problems that unfairly punished newer players that didn't know the trick. IMO parasite shouldn't be a combat tool, it should only be used for its actual purpose: tracking marines.<!--QuoteEnd--></div><!--QuoteEEnd-->
    Death to parasite (<b>after</b> two bites) is utter humiliation, though. It <b>has</b> to be that way. :P
  • PlasmaPlasma Join Date: 2003-04-26 Member: 15855Members, Constellation, Squad Five Blue
    Harimau, I wonder if its just rounding - I think you get partial resources (eg 0.1?) every few seconds, perhaps something is rounded on the UI (rounded up) when you don't really have 20 res, you may really have 19.7 res at that very moment.

    I could be wrong about that part though.
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    edited January 2011
    That's actually what I was thinking. So in his case, where it said he had "5" res, maybe he had 4.9 (rounds up to 5), and when he had "6" res, maybe he had 5.1 (rounds up to 6). Just as an example. The obvious solution, then, is just to round down instead.

    Edit: Yep. No idea. The game adds 1 (whole) carbon every 12 seconds (length of the update interval), per resource tower, beginning 4 seconds after the resource tower is built. Basically, there's no rounding involved, so there's no issue with rounding. It's plain and simple addition.

    For those who are interested in the relevant code (I worked backwards, so that's why the order is reversed):
    <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->//GUIResourceDisplay.lua
    function GUICommanderManager:CreateResourceDisplay()
        local settingsTable = { }
        settingsTable.Width = GUICommanderManager.kResourceDisplayWidth
        settingsTable.BackgroundAnchorX = GUIItem.Middle
        settingsTable.BackgroundAnchorY = GUIItem.Top
        settingsTable.X = -settingsTable.Width / 2
        settingsTable.Y = GUICommanderManager.kResourceDisplayOffset
        self.resourceDisplay = GUIResourceDisplay()
        self.resourceDisplay:Initialize(settingsTable)
    end


    //Player_Server.lua
    function GUIResourceDisplay:Update(deltaTime)
        self.plasmaText:SetText(ToString(PlayerUI_GetPlayerResources()))
        self.carbonText:SetText(ToString(PlayerUI_GetTeamResources()))
        self.towerText:SetText(ToString(CommanderUI_GetTeamHarvesterCount()))
    end


    //Player.lua
    function PlayerUI_GetTeamResources()
        local player = Client.GetLocalPlayer()
        if player then
            return player:GetDisplayTeamCarbon()
        end
        return 0
    end

    function Player:GetDisplayTeamCarbon()
        local displayTeamCarbon = self.teamCarbon
        if(Client and self.resourceDisplay) then
            displayTeamCarbon = self.animatedCarbonDisplay:GetDisplayValue()
        end
        return displayTeamCarbon

    end

    function Player:AddCarbon(amount)
        self.teamCarbon = math.max(math.min(self.teamCarbon + amount, kMaxResources), 0)
    end


    //ResourceTower_Server.lua
    function ResourceTower:UpdateOnThink()
    ...
        if(team ~= nil) then
            team:AddCarbon(ResourceTower.kCarbonInjection)        
        end
    ...
    end

    function ResourceTower:GetUpdateInterval()
        return kResourceTowerResourceInterval
    end

    function ResourceTower:OnThink()
        if self:GetIsBuilt() and self:GetIsAlive() and (self:GetAttached() ~= nil) and self:GetIsActive() and (self:GetAttached():GetAttached() == self) and GetGamerules():GetGameStarted() then
            self:UpdateOnThink()
        end
        Structure.OnThink(self)
        self:SetNextThink(self:GetUpdateInterval())

    end

    function ResourceTower:OnConstructionComplete()
        Structure.OnConstructionComplete(self)
        self:SetNextThink(ResourceTower.kBuildDelay)
    end


    //ResourceTower.lua
    ResourceTower.kCarbonInjection = 1
    ResourceTower.kBuildDelay = 4


    //Balance.lua
    kResourceTowerResourceInterval = 12<!--c2--></div><!--ec2-->
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    Regarding the above post, could there be a mismatch between what the server and client displays? (Confirmations always go through the server though.)

    Just adding the backlog. Discuss.

    <u>Resource Usage (Different Types)</u>
    All buttons are 'tech nodes'
    Press a button,
    For example, tracking the carbon cost of a sentry...
    <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->// using:
    //TechTree_Server.lua
    function TechTree:AddBuildNode(techId, prereq1, prereq2)
        local techNode = TechNode()
        techNode:Initialize(techId, kTechType.Build, prereq1, prereq2)
        techNode.requiresTarget = true
        self:AddNode(techNode)    
    end
    // so:
    //MarineTeam.lua
        self.techTree:AddBuildNode(kTechId.Sentry,              kTechId.None,                kTechId.None)
    // reads techId (specified), and prereqs, then
    // initialises kTechType.Build (specifies buildable) as well as techId, and prereqs
    // then using:
    Technode.lua
    function TechNode:GetIsBuild()
        return self.techType == kTechType.Build
    end
    // returns "true" if techtype is a buildable
    // so:
    //Commander_Server.lua
        // Handle tech tree actions that cost carbon    
        if(techNode:GetIsResearch() or techNode:GetIsUpgrade() or techNode:GetIsBuild() or techNode:GetIsEnergyBuild()) then
            local costsEnergy = techNode:GetIsEnergyBuild()
            local teamCarbon = team:GetCarbon()
            local energy = entity:GetEnergy()
            if (not costsEnergy and cost <= teamCarbon) or (costsEnergy and cost <= energy) then
                if(techNode:GetIsResearch() or techNode:GetIsUpgrade() or techNode:GetIsEnergyBuild()) then
                    success = self:AttemptToResearchOrUpgrade(techNode, force)
                    if success then
                        keepProcessing = false
                    end
                elseif(techNode:GetIsBuild()) then
                    success = self:AttemptToBuild(techId, position, pickVec, false)
                    if success then
                        keepProcessing = false
                    end
                end
                if success then
                    if costsEnergy then            
                        entity:SetEnergy(entity:GetEnergy() - cost)                
                    else                
                        team:AddCarbon(-cost)                    
                    end
                    Shared.PlayPrivateSound(self, Commander.kSpendCarbonSoundName, nil, 1.0, self:GetOrigin())
                end
            else
                self:TriggerNotEnoughResourcesAlert()
            end
    // checks if research, upgrade, buildable or energy buildable
    // checks if enough carbon or enough energy
    // builds sentry and subtracts cost; or triggers "not enough resources" alert<!--c2--></div><!--ec2-->
    (Thanks Darkster.)

    Because of the way the GUI is written now, there should never be a mismatch between the resource stated and the resource used, this is always dependent on the "technode type":
    <u>Resources (Tooltips)</u>
    For example, tracking plasma cost of an item:
    <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->//Technode.lua
    // Returns: 0 = carbon, 1 = plasma, 2 = energy (from CommanderUI_MenuButtonTooltip). Returns nil if none required.
    function TechNode:GetResourceType()
        // Carbon
        if self.techType == kTechType.Research or self.techType == kTechType.Upgrade or self.techType == kTechType.Build then
            return 0
        // Plasma
        elseif self.techType == kTechType.Buy or self.techType == kTechType.Manufacture then
            return 1
        // Energy
        elseif self.techType == kTechType.Action or self.techType == kTechType.EnergyBuild or self.techType == kTechType.Activation then
            return 2
        end
        return nil
    end
    // use GetResourceType, if kTechType.Buy, returns 1

    //Commander_Buttons.lua
    function CommanderUI_MenuButtonTooltip(index)
            if techNode then
                resourceType = techNode:GetResourceType()
            end
        return {tooltipText, hotkey, cost, requiresText, enablesText, tooltipInfo, resourceType}  
    // use CommanderUI_MenuButtonTooltip(), returns ..., resourceType

    //GUICommanderButtons.lua
    function GUICommanderButtons:UpdateTooltip(tooltipButtonIndex)
            local tooltipData = CommanderUI_MenuButtonTooltip(tooltipButtonIndex)
            local typeNumber = tooltipData[7]
            self.tooltip:UpdateData(text, hotKey, costNumber, requires, enabled, info, typeNumber)
    end

    //GUICommanderTooltip.lua
    function GUICommanderTooltip:UpdateData(text, hotkey, costNumber, requires, enables, info, typeNumber)
            self.resourceIcon:SetTexturePixelCoordinates(0, typeNumber * GUICommanderTooltip.kResourceIconTextureHeight,
                                                         GUICommanderTooltip.kResourceIconTextureWidth,
                                                         (typeNumber + 1) * GUICommanderTooltip.kResourceIconTextureHeight)
            self.cost:SetColor(GUICommanderTooltip.kResourceColors[typeNumber + 1])
    end
    // won't bother tracking any further, pretty sure this just prints the plasma icon<!--c2--></div><!--ec2-->

    <u>Technode Types</u>
    Some comments from TechNode.lua:
    <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->// Represents one item in the tech tree. They also characterize the behavior when triggering that kind of action.
    // Tech nodes are of the following types:
    //
    //   Order (Move, Default) - No cost, requires positional
    //   Research (eg, research siege) - Costs carbon, queued from a structure, non-positional
    //   Upgrade (eg, upgrade command station) - Like research but can be performed more than once
    //   Action (eg, medpack, drifter flare) - Optionally costs energy, optional position, optionally must be researched
    //   Buy (eg, create siege cannon from factory, player buy weapon) - Costs plasma, position implied from buyer or originating structure (unless targeted). Requires target for commander.
    //   Build (eg, build structure from drifter) - Costs carbon, requires position
    //   EnergyBuild (add to manufacture queue, create unit when done) - Costs energy, takes time to complete, no position (MACs, Drifters). Only use for AI units that don't need a position.
    //   Manufacture (add to manufacture queue, create unit when done) - Costs plasma, takes time to complete, no position (ARCs)
    //   Activation (eg, deploy/undeploy siege) - Optionally costs energy, optional position
    //   Menu - No cost, no position<!--c2--></div><!--ec2-->
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    <u>MedPacks, the Armoury, and AddHealth</u>
    <!--quoteo(post=1824110:date=Jan 14 2011, 05:08 AM:name=Kouji_San)--><div class='quotetop'>QUOTE (Kouji_San @ Jan 14 2011, 05:08 AM) <a href="index.php?act=findpost&pid=1824110"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec--><!--quoteo(post=1824100:date=Jan 14 2011, 04:33 AM:name=Stele007)--><div class='quotetop'>QUOTE (Stele007 @ Jan 14 2011, 04:33 AM) <a href="index.php?act=findpost&pid=1824100"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->I could be wrong, but I believe right now medpacks do heal armor.<!--QuoteEnd--></div><!--QuoteEEnd-->
    It seems to only repair armor, when your health is also below 100 and there seems to be a bug. When your health is at 100, but your armor is not at full, you still pick up medkits, while not repairing your armor.

    Reported this at <a href="http://getsatisfaction.com/unknownworlds/topics/medkits_being_picked_up_at_full_health" target="_blank">Getsatisaction</a>
    <!--QuoteEnd--></div><!--QuoteEEnd-->
    <!--quoteo(post=1824197:date=Jan 14 2011, 10:23 AM:name=Harimau)--><div class='quotetop'>QUOTE (Harimau @ Jan 14 2011, 10:23 AM) <a href="index.php?act=findpost&pid=1824197"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->Medpacks and the Armoury use the same function: AddHealth
    The function adds both health and armour (but health first).

    In the case of both the Armoury and the Medpack, it checks if you're alive and, if you have less than max hp OR less than max armour. Medpack also checks whether you are parasited, and removes it if you are.

    AddHealth takes an amount (50 for the medpack), adds that to your health, but if your health was 70 for example, it'd only add 30 (because maxhealth is 100), but the remainder (20) would go onto your armour.

    I'm not sure why the medpack doesn't add more armour when you're on maxhp though, it's supposed to. The armoury works properly anyway.

    Also, can anyone confirm that the medpack does remove parasite?<!--QuoteEnd--></div><!--QuoteEEnd-->
    <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->//MedPack_Server.lua
    function MedPack:OnTouch(player)
    ...
            // If player has less than full health or is parasited
            if( (player:GetHealth() < player:GetMaxHealth()) or (player:GetArmor() < player:GetMaxArmor()) or player:GetGameEffectMask(kGameEffect.Parasite) ) then
                player:AddHealth(MedPack.kHealth)
                player:SetGameEffectMask(kGameEffect.Parasite, false)
    ...


    //Armory_Server.lua
    function Armory:ResupplyPlayer(player)
        local resuppliedPlayer = false
        // Heal player first
        if( (player:GetHealth() < player:GetMaxHealth()) or (player:GetArmor() < player:GetMaxArmor()) ) then
            player:AddHealth(Armory.kHealAmount)
            player:TriggerEffects("armory_health")
            resuppliedPlayer = true    
        end
    ...


    //LiveScriptActor_Server.lua
    function LiveScriptActor:AddHealth(health, playSound)
    ...    
        if self:GetIsAlive() and ((self.health < self:GetMaxHealth()) or (self.armor < self:GetMaxArmor())) then
            // Add health first, then armor if we're full
            local healthAdded = math.min(health, self:GetMaxHealth() - self.health)
            self.health = math.min(math.max(0, self.health + healthAdded), self:GetMaxHealth())
            local healthToAddToArmor = health - healthAdded
            if(healthToAddToArmor > 0) then        
                self.armor = math.min(math.max(0, self.armor + healthToAddToArmor), self:GetMaxArmor())  
            end
    ...<!--c2--></div><!--ec2-->

    <u>Lerk Spores</u>
    <!--quoteo(post=1823853:date=Jan 13 2011, 02:03 PM:name=Harimau)--><div class='quotetop'>QUOTE (Harimau @ Jan 13 2011, 02:03 PM) <a href="index.php?act=findpost&pid=1823853"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->From the perspective of the code: It has to do with the armour/damage system**. Although the damage type is 'biological' (read: does not affect structures or exoskeletons), the same damage is applied in the same way as the 'normal' damage type**, to these biological targets:
    7 damage, every 0.5 seconds, for 6 seconds (from SporeCloud.lua), i.e. 12 ticks of 7 damage (alternatively can be expressed as 14 damage per second, from Balance.lua - haven't checked which is used, but I assume SporeCloud.lua):
    <img src="http://img3.imageshack.us/img3/7808/ns2sporevsmarine.png" border="0" class="linked-image" />
    ** <b>This is because there isn't (yet) any specific code applied</b> for damage types other than 'heavy' (e.g. pistol), 'light' (e.g. sentry), and 'normal' (e.g. rifle, bite, swipe, most attacks), but 'normal' damage-dealing also includes every non-'heavy' and non-'light' damage type. These all use the same code, really, but have different HealthPerArmor values: 1 for heavy, 2 for normal, 4 for light. The higher the number, the more health that 1 point of armour protects.

    I would imagine that 'biological' would have a zero HealthPerArmor value, i.e. 1 point of armor protects no health. Knowing this, this is actually very easy to code.
    (This is one approach.)
    <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->// add to Balance.lua:
    kHealthPointsPerArmorBiological = 0

    // change to Balance.lua:
    kSporesDamageType = kDamageType.Biological

    // add to function LiveScriptActor:GetHealthPerArmor(damageType)
        elseif damageType == kDamageType.Biological then
            healthPerArmor = kHealthPointsPerArmorBiological<!--c2--></div><!--ec2-->
    So:
    <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->        local absorbPercentage = self:GetArmorAbsorbPercentage(damageType)
            healthPointsBlocked = math.min(self:GetHealthPerArmor(damageType) * self.armor, absorbPercentage * damage )
            armorPointsUsed = healthPointsBlocked / self:GetHealthPerArmor(damageType)
            healthPointsUsed = damage - healthPointsBlocked
    =>
            local absorbPercentage = self:GetArmorAbsorbPercentage(damageType)
            healthPointsBlocked = math.min(0 * self.armor, absorbPercentage * damage ) = 0
            armorPointsUsed = 0 / 0 = ?
            healthPointsUsed = damage - 0 = damage<!--c2--></div><!--ec2-->
    That middle line that tries to equate zero divided by zero could become a problem though. You could easily add an if statement to fix it where:
    IF self:GetHealthPerArmor(damageType) == 0, THEN armorPointsUsed = 0, ELSE do the same as before.

    An alternative approach is to set absorbPercentage to zero:
    <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->// change to Balance.lua:
    kSporesDamageType = kDamageType.Biological

    // modify function LiveScriptActor:GetArmorAbsorbPercentage(damageType)
        if damageType == kDamageType.Falling or damageType == kDamageType.Biological then
            armorAbsorbPercentage = 0<!--c2--></div><!--ec2-->
    So:
    <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->        local absorbPercentage = self:GetArmorAbsorbPercentage(damageType)
            healthPointsBlocked = math.min(self:GetHealthPerArmor(damageType) * self.armor, absorbPercentage * damage )
            armorPointsUsed = healthPointsBlocked / self:GetHealthPerArmor(damageType)
            healthPointsUsed = damage - healthPointsBlocked
    =>
            local absorbPercentage = 0
            healthPointsBlocked = math.min(self:GetHealthPerArmor(damageType) * self.armor, 0 * damage ) = 0
            armorPointsUsed = 0 / self:GetHealthPerArmor(damageType) = 0
            healthPointsUsed = damage - 0 = damage<!--c2--></div><!--ec2-->
    In either approach, healthPointsBlocked is 0, so spores do full damage to health, and amorPointsUsed is 0, so no damage to armour.

    <b>No armour effects on spores seems to already be planned though</b>:
    // Armor is best at absorbing melee damage, less against projectiles and not effective for gas/breathing damage
    from LiveScriptActor_Server.lua<!--QuoteEnd--></div><!--QuoteEEnd-->
  • Antonio Gramscix420Antonio Gramscix420 Join Date: 2010-07-19 Member: 72554Members
    an analysis of your face: i dont' like it.
  • KoruyoKoruyo AUT Join Date: 2009-06-06 Member: 67724Members, Reinforced - Shadow
    edited January 2011
    <!--quoteo(post=1824681:date=Jan 16 2011, 01:46 AM:name=Antonio Gramscix420)--><div class='quotetop'>QUOTE (Antonio Gramscix420 @ Jan 16 2011, 01:46 AM) <a href="index.php?act=findpost&pid=1824681"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->an analysis of your face: i dont' like it.<!--QuoteEnd--></div><!--QuoteEEnd-->

    <img src="http://www.threadbombing.com/data/media/13/75378-TrollFace.png" border="0" class="linked-image" />

    Im sorry - i had to :((
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    edited January 2011
    <!--quoteo(post=1824681:date=Jan 16 2011, 08:46 AM:name=Antonio Gramscix420)--><div class='quotetop'>QUOTE (Antonio Gramscix420 @ Jan 16 2011, 08:46 AM) <a href="index.php?act=findpost&pid=1824681"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->an analysis of your face: i dont' like it.<!--QuoteEnd--></div><!--QuoteEEnd-->
    If that's you, I can say the same back.
  • twilitebluetwiliteblue bug stalker Join Date: 2003-02-04 Member: 13116Members, NS2 Playtester, Squad Five Blue
    edited January 2011
    Here's some data I dug up.

    <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->Skulk.Lua:
    Skulk.kViewOffsetHeight = .55

    Player.Lua:
    Player.kJumpHeight =  1    //Marines have the same jump height<!--c2--></div><!--ec2-->


    The problem arises when:
    <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->BiteLeap.lua:
    BiteLeap.kRange = .6

    Axe.lua:
    Axe.kRange = 1.0    // Hmmm

    Rifle.lua:
    Rifle.kButtRange = 1.5    // Whoa

    ////////////////////////////////////////////////

    Balance.lua
    //Have to take these into account as well
    kRifleMeleeDamage = 35    
    kRifleMeleeDamageType = kDamageType.Normal    
    kRifleMeleeFireDelay = 0.7

    kBiteDamage = 75    
    kBiteDamageType = kDamageType.Normal    
    kBiteFireDelay = 0.45<!--c2--></div><!--ec2-->

    So it is possible for a marine to 3 shot a skulk in melee range with a Rifle (although rifle butt attack is slower than bite, but the marine is more likely to land hits due to the range bonus). Now that it takes a Skulk 3 bites to kill a marine, marines are no longer instant free meals, even in melee range.

    The problem is that <b>Marines can jump over Skulks in melee fights</b> (it's not much of a fight if they aren't in melee), often confusing the skulk, combined with their <b>superior range</b>(even melee range), give Marines a big advantage in melee fights. As a result, I'm seeing more rifle butt kills than ever.
  • KoruyoKoruyo AUT Join Date: 2009-06-06 Member: 67724Members, Reinforced - Shadow
    edited January 2011
    You see more rifle butt kills because hit detection while moving/dodging got worse => making marines use melee more often after getting a few lucky hits with the rifle.

    => You have a lot jump around situations that shouldnt be there... i had fights alone as a marine against 3 skulks that lasted nearly a 1min because nobody could hit the other and we where just jumping around.

    Lerks are now more or less unkillable if not played like a static turret by a noob.

    IF players can move, fights take a lot longer in 161, if they cant... marines shoot your ass faster than you can say "oh shi.t"
  • JaweeseJaweese Join Date: 2006-11-04 Member: 58356Members
    Armor is still bugged in 162, but not in the same way

    Damage varies depending on whether you have armor or not. If you have armor, you'll take less total damage than if you have no armor. It affects both marines and aliens.

    For example, an armor 0 marine against a whip, one hit deals 15 health and 9 armor, for a total of 33 damage. When you have no armor, it will deal 50 damage.


    This is probably why it feels easier to kill skulks in 162. They die in 4 pistol hits (3 in secondary mode).
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    edited January 2011
    That's not wrong. This is the <b>intended</b> way. Armour blocks more damage (and is consumed less) than health, because it's not 1 to 1.

    For example,
    In your example, a whip deals 50 damage, correct?
    The whip does 'light' damage, so the damage blocked per point of armour is 4.
    The armour absorption percentage is 70%.
    So 50 damage is split into:
    70% goes to armour, 30% goes to health.
    or, 35 goes to armour, 15 goes to health.
    But the damage blocked per point of armour is 4, so of the 35 damage that goes to armour, only 35/4 armour points are consumed: 8.75 (rounded up to 9)

    In summary: the marine's armour absorbs 35 damage, but 8.75 (or 9) points of armour are consumed. Direct health damage is 15.
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    edited January 2011
    :/ Double post.

    I addressed the pistol thing earlier. Lemme bring up a quote.

    <!--quoteo(post=1822825:date=Jan 9 2011, 01:33 PM:name=Harimau)--><div class='quotetop'>QUOTE (Harimau @ Jan 9 2011, 01:33 PM) <a href="index.php?act=findpost&pid=1822825"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec--><u>Edit2</u>:
    <u>Pistol vs skulk</u>:
    Taking into account that HealthPerArmor for pistol is 1 (Heavy damage-type), ironically pistol damage on skulks actually works properly:

    Default fire mode:
    shot 1: damage = 20 -> healthpointsblocked = 10 (min(10,14)) -> armorpointsused = 10 (<b>0</b> remaining) -> healthpointsused = 10 (<b>60</b> remaining)
    shot 2: damage = 20 -> healthpointsblocked = 0 (min(0,14)) -> armorpointused = 0 (<b>0</b> remaining) -> healthpointsused = 20 (<b>40</b> remaining)
    shot 3: damage = 20 -> healthpointsblocked = 0 (min(0,14)) -> armorpointused = 0 (<b>0</b> remaining) -> healthpointsused = 20 (<b>20</b> remaining)
    shot 4: damage = 20 -> healthpointsblocked = 0 (min(0,14)) -> armorpointused = 0 (<b>0</b> remaining) -> healthpointsused = 20 (<b>0</b> remaining)
    Ostensibly can be done in 0.4 seconds: faster than a skulk can bite (0.45 seconds).

    Alternate fire mode:
    shot 1: damage = 30 -> healthpointsblocked = 10 (min(10,21)) -> armorpointsused = 10 (<b>0</b> remaining) -> healthpointsused = 20 (<b>50</b> remaining)
    shot 2: damage = 30 -> healthpointsblocked = 0 (min(0,21)) -> armorpointsused = 0 (<b>0</b> remaining) -> healthpointsused = 30 (<b>20</b> remaining)
    shot 3: damage = 30 -> healthpointsblocked = 0 (min(0,21)) -> armorpointsused = 0 (<b>0</b> remaining) -> healthpointsused = 30 (<b>0</b> remaining)
    Ostensibly can be done in 0.6 seconds.

    I think that the best option for a marine currently, considering the uselessness of the rifle, is to just take skulks out with the pistol: 3 well-placed shots with alt-fire mode. Hell, don't marines run out of rifle ammunition then have to kill skulks with the pistol anyway?<!--QuoteEnd--></div><!--QuoteEEnd-->

    In short, it's not exactly 'easier to kill skulks', the problem is that it isn't a level playing field. Marines can now fire and register hits, but skulks can't bite and register hits, especially with crazy marine hopping. It's not exactly easier to kill skulks, it's just hard for skulks to kill marines; once the playing field is level, they should have about an equal chance against one another.
  • twilitebluetwiliteblue bug stalker Join Date: 2003-02-04 Member: 13116Members, NS2 Playtester, Squad Five Blue
    Lag compensation seems to be working well, as I actually get a lot of kills on laggy (ping >300) servers. Fast moving Skulks tend to warp from one location to another, and I can get the shots in when they appear to pause after each jump.

    The problem lies with client interpolation, I think.
  • JaweeseJaweese Join Date: 2006-11-04 Member: 58356Members
    Ah, I forgot about damage types. I was very confused for a moment there.
Sign In or Register to comment.