diff --git a/core/lua/Actor.lua b/core/lua/Actor.lua
index 9959fd9..9a47321 100644
--- a/core/lua/Actor.lua
+++ b/core/lua/Actor.lua
@@ -15,14 +15,6 @@ class 'Actor' (Entity)
Actor.kMapName = "actor"
-Actor.PhysicsType = enum
- {
- 'None', // No physics representation.
- 'Dynamic', // Bones are driven by physics simulation (client-side only)
- 'DynamicServer', // Bones are driven by physics simulation (synced with server)
- 'Kinematic' // Physics model is updated by animation
- }
-
// Maximum number of animations we support on in a model. This is
// limited for the sake of propagating animation indices.
Actor.maxAnimations = 72
@@ -38,7 +30,7 @@ Actor.networkVars =
// Reset to 1 every time a new animation is set.
animationSpeed = "compensated float",
- physicsType = "enum Actor.PhysicsType",
+ physicsType = "enum PhysicsType",
physicsGroup = "integer (0 to 31)",
}
@@ -64,6 +56,8 @@ function Actor:OnCreate()
Entity.OnCreate(self)
+ InitMixin(self, TimedCallbackMixin)
+
self.modelIndex = 0
self.animationSequence = Model.invalidSequence
self.animationStart = 0
@@ -75,7 +69,7 @@ function Actor:OnCreate()
self.animationSpeed = 1.0
self.boneCoords = CoordsArray()
self.poseParams = PoseParams()
- self.physicsType = Actor.PhysicsType.None
+ self.physicsType = PhysicsType.None
self.physicsModel = nil
self.physicsGroup = 0 //PhysicsGroup.DefaultGroup
@@ -103,8 +97,6 @@ end
function Actor:OnInit()
- InitMixin(self, TimedCallbackMixin)
-
if Client then
self:TriggerEffects("on_init")
@@ -238,17 +230,17 @@ function Actor:UpdatePhysicsModelSimulation()
if (self.physicsModel ~= nil) then
- if (self.physicsType == Actor.PhysicsType.None) then
+ if (self.physicsType == PhysicsType.None) then
self.physicsModel:SetPhysicsType(CollisionObject.None)
- elseif (self.physicsType == Actor.PhysicsType.DynamicServer) then
+ elseif (self.physicsType == PhysicsType.DynamicServer) then
if (Server) then
self.physicsModel:SetPhysicsType(CollisionObject.Dynamic)
else
self.physicsModel:SetPhysicsType(CollisionObject.Kinematic)
end
- elseif (self.physicsType == Actor.PhysicsType.Dynamic) then
+ elseif (self.physicsType == PhysicsType.Dynamic) then
self.physicsModel:SetPhysicsType(CollisionObject.Dynamic)
- elseif (self.physicsType == Actor.PhysicsType.Kinematic) then
+ elseif (self.physicsType == PhysicsType.Kinematic) then
self.physicsModel:SetPhysicsType(CollisionObject.Kinematic)
end
@@ -259,9 +251,9 @@ end
function Actor:GetIsDynamic()
if (Server) then
- return self.physicsType == Actor.PhysicsType.DynamicServer
+ return self.physicsType == PhysicsType.DynamicServer
else
- return self.physicsType == Actor.PhysicsType.Dynamic
+ return self.physicsType == PhysicsType.Dynamic
end
end
@@ -592,13 +584,6 @@ function Actor:OnUpdate(deltaTime)
end
- // Needed because of the problem where Actors can be created and updated on the client before being inited.
- // "Spit created/destroyed in a single update" problem. Once that is fixed, this check won't be needed.
- if HasMixin(self, "TimedCallback") then
- // From TimedCallbackMixin.
- self:UpdateTimedCallbacks(deltaTime)
- end
-
end
if (Client) then
@@ -670,9 +655,9 @@ function Actor:UpdatePhysicsModelCoords()
if (self.physicsModel ~= nil) then
- local update = self.physicsType == Actor.PhysicsType.Kinematic or self.physicsType == Actor.PhysicsType.None
+ local update = self.physicsType == PhysicsType.Kinematic or self.physicsType == PhysicsType.None
- if Client and self.physicsType == Actor.PhysicsType.DynamicServer then
+ if Client and self.physicsType == PhysicsType.DynamicServer then
update = true
end
@@ -909,7 +894,7 @@ function Actor:OnUpdatePhysics()
self:UpdateBoneCoords()
self:UpdatePhysicsModel()
- if (self.physicsType ~= Actor.PhysicsType.None) then
+ if (self.physicsType ~= PhysicsType.None) then
if (self.physicsModel ~= nil and self:GetIsDynamic()) then
diff --git a/ns2/gamestrings/enUS.txt b/ns2/gamestrings/enUS.txt
index 6c98c4c..0d1cf6b 100644
--- a/ns2/gamestrings/enUS.txt
+++ b/ns2/gamestrings/enUS.txt
@@ -206,10 +206,10 @@ ENERGIZE = "Energize"
DISORIENT = "Disorient"
CLOACK = "Cloak"
-PHANTASM = "Phantasm"
-PHANTASM_FADE = "Phantasm Fade"
-PHANTASM_ONOS = "Phantasm Onos"
-PHANTASM_HIVE = "Phantasm Hive"
+PHANTOM = "Phantom"
+PHANTOM_FADE = "Phantom Fade"
+PHANTOM_ONOS = "Phantom Onos"
+PHANTOM_HIVE = "Phantom Hive"
UNROOT_WHIP = "Unroot Whip"
ROOT_WHIP = "Root Whip"
@@ -323,8 +323,8 @@ SHIFT_TOOLTIP = "Speeds energy recovery for units and shift units around map"
UPGRADE_SHIFT_TOOLTIP = "Increase Shift health and gain Echo ability"
MATURE_SHIFT_TOOLTIP = "Shift with Echo ability"
SHADE_TOOLTIP = "Cloaks nearby units and allows deception upgrades"
-UPGRADE_SHADE_TOOLTIP = "Increase Shade health and grant Phantasm ability"
-MATURE_SHADE_TOOLTIP = "Shade with Phantasm ability"
+UPGRADE_SHADE_TOOLTIP = "Increase Shade health and grant Phantom ability"
+MATURE_SHADE_TOOLTIP = "Shade with Phantom ability"
CRAG_HEAL_TOOLTIP = "Heals players and structures (+10 every 2 seconds, max 3 targets)"
SHIFT_ECHO_TOOLTIP = "Reposition structure elsewhere"
SHIFT_RECALL_TOOLTIP = "Aliens can use the shift to teleport to the nearest hive"
@@ -445,3 +445,4 @@ FOLLOWING = "Following %s"
BEACONING = "Commander issued Distress Beacon. Teleport imminent."
BEACONING_COMMANDER = "Distress Beacon triggered. Teleport imminent."
TOO_MANY_ENTITES = "Too many entities in area."
+ALIEN_HUD_PHANTOM = "Phantom (no damage)"
diff --git a/ns2/lua/ARC.lua b/ns2/lua/ARC.lua
index 77a5212..254551d 100644
--- a/ns2/lua/ARC.lua
+++ b/ns2/lua/ARC.lua
@@ -12,8 +12,12 @@
Script.Load("lua/LiveScriptActor.lua")
Script.Load("lua/DoorMixin.lua")
Script.Load("lua/mixins/ControllerMixin.lua")
+Script.Load("lua/RagdollMixin.lua")
+Script.Load("lua/UpgradableMixin.lua")
+Script.Load("lua/PointGiverMixin.lua")
Script.Load("lua/GameEffectsMixin.lua")
Script.Load("lua/FlinchMixin.lua")
+Script.Load("lua/SelectableMixin.lua")
Script.Load("lua/TargetMixin.lua")
class 'ARC' (LiveScriptActor)
@@ -67,6 +71,7 @@ ARC.networkVars =
targetDirection = "vector",
}
+PrepareClassForMixin(ARC, UpgradableMixin)
PrepareClassForMixin(ARC, GameEffectsMixin)
PrepareClassForMixin(ARC, FlinchMixin)
@@ -75,10 +80,13 @@ function ARC:OnCreate()
LiveScriptActor.OnCreate(self)
InitMixin(self, ControllerMixin)
+ InitMixin(self, RagdollMixin)
+ InitMixin(self, UpgradableMixin)
InitMixin(self, GameEffectsMixin)
InitMixin(self, FlinchMixin)
+ InitMixin(self, PointGiverMixin)
InitMixin(self, PathingMixin)
-
+ InitMixin(self, SelectableMixin)
if Server then
InitMixin(self, TargetMixin)
end
@@ -103,7 +111,7 @@ function ARC:OnInit()
{ kMarineStaticTargets, kMarineMobileTargets },
{ self.FilterTarget(self) })
- self:SetPhysicsType(Actor.PhysicsType.Kinematic)
+ self:SetPhysicsType(PhysicsType.Kinematic)
// Cannons start out mobile
self:SetDesiredMode(ARC.kMode.UndeployedStationary)
@@ -211,7 +219,7 @@ function ARC:GetInAttackMode()
return (self.mode == ARC.kMode.Deployed or self.mode == ARC.kMode.Firing or self.mode == ARC.kMode.Targeting or self.mode == ARC.kMode.FireCooldown) and self.desiredMode ~= ARC.kMode.UndeployedStationary
end
-function ARC:GetCanDoDamage()
+function ARC:GetCanGiveDamageOverride()
return true
end
diff --git a/ns2/lua/Alien.lua b/ns2/lua/Alien.lua
index 3db002a..4d6e182 100644
--- a/ns2/lua/Alien.lua
+++ b/ns2/lua/Alien.lua
@@ -10,6 +10,7 @@
Script.Load("lua/Player.lua")
Script.Load("lua/CloakableMixin.lua")
Script.Load("lua/CamouflageMixin.lua")
+Script.Load("lua/PhantomMixin.lua")
class 'Alien' (Player)
Alien.kMapName = "alien"
@@ -65,6 +66,7 @@ Alien.networkVars =
PrepareClassForMixin(Alien, CloakableMixin)
PrepareClassForMixin(Alien, CamouflageMixin)
+PrepareClassForMixin(Alien, PhantomMixin)
function Alien:OnCreate()
@@ -87,6 +89,7 @@ function Alien:OnInit()
InitMixin(self, CloakableMixin)
InitMixin(self, CamouflageMixin)
+ InitMixin(self, PhantomMixin)
self.abilityEnergy = Ability.kMaxEnergy
diff --git a/ns2/lua/AlienTeam.lua b/ns2/lua/AlienTeam.lua
index 249e538..b56e792 100644
--- a/ns2/lua/AlienTeam.lua
+++ b/ns2/lua/AlienTeam.lua
@@ -131,7 +131,7 @@ function AlienTeam:UpdateTeamAutoHeal(timePassed)
if self.timeOfLastAutoHeal == nil or (time > (self.timeOfLastAutoHeal + AlienTeam.kAutoHealInterval)) then
// Heal all players by this amount
- local teamEnts = GetEntitiesForTeam("LiveScriptActor", self:GetTeamNumber())
+ local teamEnts = GetEntitiesWithMixinForTeam("Live", self:GetTeamNumber())
for index, entity in ipairs(teamEnts) do
@@ -179,7 +179,7 @@ function AlienTeam:GetBlipType(entity)
local blipType = kBlipType.Undefined
- if entity:isa("LiveScriptActor") and entity:GetIsVisible() and entity:GetIsAlive() and not entity:isa("Infestation") then
+ if entity:GetIsVisible() and HasMixin(entity, "Live") and entity:GetIsAlive() and not entity:isa("Infestation") then
if entity:GetTeamNumber() == self:GetTeamNumber() then
@@ -478,7 +478,7 @@ function AlienTeam:InitTechTree()
// Add special alien menus
self.techTree:AddMenu(kTechId.MarkersMenu)
self.techTree:AddMenu(kTechId.UpgradesMenu)
- self.techTree:AddMenu(kTechId.ShadePhantasmMenu)
+ self.techTree:AddMenu(kTechId.ShadePhantomMenu)
// Add markers (orders)
self.techTree:AddSpecial(kTechId.ThreatMarker, true)
@@ -551,16 +551,14 @@ function AlienTeam:InitTechTree()
// Shade
self.techTree:AddUpgradeNode(kTechId.UpgradeShade, kTechId.Shade, kTechId.None)
- self.techTree:AddBuildNode(kTechId.MatureShade, kTechId.None, kTechId.None)
+ self.techTree:AddBuildNode(kTechId.MatureShade, kTechId.TwoHives, kTechId.None)
self.techTree:AddActivation(kTechId.ShadeDisorient, kTechId.None, kTechId.None)
self.techTree:AddActivation(kTechId.ShadeCloak, kTechId.None, kTechId.None)
- // Shade targeted abilities - treat phantasms as build nodes so we show ghost and attach points for fake hive
- self.techTree:AddResearchNode(kTechId.PhantasmTech, kTechId.MatureShade, kTechId.None)
- self.techTree:AddBuildNode(kTechId.ShadePhantasmFade, kTechId.PhantasmTech, kTechId.MatureShade)
- self.techTree:AddBuildNode(kTechId.ShadePhantasmOnos, kTechId.None, kTechId.None)
- self.techTree:AddBuildNode(kTechId.ShadePhantasmHive, kTechId.PhantasmTech, kTechId.MatureShade)
-
+ // Shade targeted abilities - treat Phantoms as build nodes so we show ghost and attach points for fake hive
+ self.techTree:AddResearchNode(kTechId.PhantomTech, kTechId.MatureShade, kTechId.None)
+ self.techTree:AddBuildNode(kTechId.ShadePhantomFade, kTechId.PhantomTech, kTechId.MatureShade)
+ self.techTree:AddBuildNode(kTechId.ShadePhantomOnos, kTechId.PhantomTech, kTechId.None)
// Crag upgrades
self.techTree:AddResearchNode(kTechId.AlienArmor1Tech, kTechId.Crag, kTechId.None)
diff --git a/ns2/lua/AlienWeaponEffects.lua b/ns2/lua/AlienWeaponEffects.lua
index 1f44980..b8ce640 100644
--- a/ns2/lua/AlienWeaponEffects.lua
+++ b/ns2/lua/AlienWeaponEffects.lua
@@ -354,11 +354,12 @@ kAlienWeaponEffects =
{
sporesAttackEffects =
{
- {looping_sound = "sound/ns2.fev/alien/lerk/spore_spray"},
{overlay_animation = "spore"},
- //{viewmodel_cinematic = "cinematics/alien/lerk/spore_view_fire.cinematic", attach_point = "?"},
- //{weapon_cinematic = "cinematics/alien/lerk/spores.cinematic", attach_point = "?"},
+ {viewmodel_cinematic = "cinematics/alien/lerk/spore_view_fire.cinematic", attach_point = "fxnode_hole_left"},
+ {viewmodel_cinematic = "cinematics/alien/lerk/spore_view_fire.cinematic", attach_point = "fxnode_hole_right"},
+ {weapon_cinematic = "cinematics/alien/lerk/spore_fire.cinematic", attach_point = "fxnode_hole_left"},
+ {weapon_cinematic = "cinematics/alien/lerk/spore_fire.cinematic", attach_point = "fxnode_hole_right"},
},
},
diff --git a/ns2/lua/Alien_Client.lua b/ns2/lua/Alien_Client.lua
index 745d073..807bd20 100644
--- a/ns2/lua/Alien_Client.lua
+++ b/ns2/lua/Alien_Client.lua
@@ -420,8 +420,8 @@ end
// Bring up evolve menu
function Alien:Buy()
- // Don't allow display in the ready room
- if self:GetTeamNumber() ~= 0 and (Client.GetLocalPlayer() == self) then
+ // Don't allow display in the ready room, or as phantom
+ if self:GetTeamNumber() ~= 0 and (Client.GetLocalPlayer() == self) and (not HasMixin(self, "Phantom") or not self:GetIsPhantom()) then
if not self.buyMenu then
self.buyMenu = GetGUIManager():CreateGUIScript("GUIAlienBuyMenu")
diff --git a/ns2/lua/Armory.lua b/ns2/lua/Armory.lua
index ba8e505..1f17536 100644
--- a/ns2/lua/Armory.lua
+++ b/ns2/lua/Armory.lua
@@ -6,6 +6,8 @@
//
// ========= For more information, visit us at http://www.unknownworlds.com =====================
Script.Load("lua/Structure.lua")
+Script.Load("lua/RagdollMixin.lua")
+
class 'Armory' (Structure)
Armory.kMapName = "armory"
@@ -35,7 +37,7 @@ else
Script.Load("lua/Armory_Client.lua")
end
-local networkVars =
+Armory.networkVars =
{
// How far out the arms are for animation (0-1)
loggedInEast = "boolean",
@@ -59,6 +61,14 @@ function GetArmory(entity)
end
+function Armory:OnCreate()
+
+ Structure.OnCreate(self)
+
+ InitMixin(self, RagdollMixin)
+
+end
+
function Armory:OnInit()
self:SetModel(Armory.kModelName)
@@ -218,7 +228,7 @@ function Armory:OnUpdate(deltaTime)
end
-Shared.LinkClassToMap("Armory", Armory.kMapName, networkVars)
+Shared.LinkClassToMap("Armory", Armory.kMapName, Armory.networkVars)
class 'AdvancedArmory' (Armory)
diff --git a/ns2/lua/ArmsLab.lua b/ns2/lua/ArmsLab.lua
index 622e01a..0d49353 100644
--- a/ns2/lua/ArmsLab.lua
+++ b/ns2/lua/ArmsLab.lua
@@ -6,11 +6,21 @@
//
// ========= For more information, visit us at http://www.unknownworlds.com =====================
Script.Load("lua/Structure.lua")
+Script.Load("lua/RagdollMixin.lua")
+
class 'ArmsLab' (Structure)
ArmsLab.kMapName = "armslab"
ArmsLab.kModelName = PrecacheAsset("models/marine/arms_lab/arms_lab.model")
+function ArmsLab:OnCreate()
+
+ Structure.OnCreate(self)
+
+ InitMixin(self, RagdollMixin)
+
+end
+
function ArmsLab:GetTechButtons(techId)
return { kTechId.Weapons1, kTechId.Weapons2, kTechId.Weapons3, kTechId.CatPackTech,
diff --git a/ns2/lua/AttackOrderMixin.lua b/ns2/lua/AttackOrderMixin.lua
new file mode 100644
index 0000000..c2542ac
--- /dev/null
+++ b/ns2/lua/AttackOrderMixin.lua
@@ -0,0 +1,181 @@
+// ======= Copyright © 2003-2011, Unknown Worlds Entertainment, Inc. All rights reserved. =======
+//
+// lua\AttackOrderMixin.lua
+//
+// Created by: Brian Cronin (brianc@unknownworlds.com)
+//
+// ========= For more information, visit us at http://www.unknownworlds.com =====================
+
+Script.Load("lua/FunctionContracts.lua")
+
+/**
+ * AttackOrderMixin handles processing attack orders.
+ */
+AttackOrderMixin = { }
+AttackOrderMixin.type = "AttackOrder"
+
+AttackOrderMixin.expectedMixins =
+{
+ Orders = "Needed for calls to GetCurrentOrder().",
+ Pathing = "Needed for calls to MoveToTarget().",
+ GameEffects = "Needed for calls to AdjustAttackDelay()."
+}
+
+AttackOrderMixin.expectedCallbacks =
+{
+ GetMeleeAttackDamage = "Returns the amount of damage each melee hit does.",
+ GetMeleeAttackInterval = "Returns how often this Entity melee attacks.",
+ GetMeleeAttackOrigin = "Returns where the melee attack originates from.",
+ TriggerEffects = "The melee_attack effect will be triggered through this callback.",
+ GetOwner = "Returns the owner, if any, of this Entity."
+}
+
+AttackOrderMixin.expectedConstants =
+{
+ kMoveToDistance = "The distance at which the move part of the Attack order is complete."
+}
+
+function AttackOrderMixin:__initmixin()
+
+ self.timeOfLastAttackOrder = 0
+
+end
+
+// This is an "attack-move" from RTS. Attack the entity specified in our current attack order, if any.
+// Otherwise, move to the location specified in the attack order and attack anything along the way.
+function AttackOrderMixin:ProcessAttackOrder(targetSearchDistance, moveSpeed, time)
+
+ // If we have a target, attack it.
+ local currentOrder = self:GetCurrentOrder()
+ if currentOrder ~= nil then
+
+ local target = Shared.GetEntity(currentOrder:GetParam())
+
+ if target then
+
+ // How do you kill that which has no life?
+ if not HasMixin(target, "Live") or not target:GetIsAlive() then
+ self:CompletedCurrentOrder()
+ else
+
+ local targetLocation = target:GetEngagementPoint()
+ if self:GetIsFlying() then
+ targetLocation = GetHoverAt(self, targetLocation)
+ end
+
+ self:MoveToTarget(PhysicsMask.AIMovement, targetLocation, moveSpeed, time)
+
+ end
+
+ else
+
+ // Check for a nearby target. If not found, move towards destination.
+ target = self:_FindTarget(targetSearchDistance)
+
+ end
+
+ if target and HasMixin(target, "Live") then
+
+ // If we are close enough to target, attack it
+ local targetPosition = Vector(target:GetOrigin())
+ if self.GetHoverHeight then
+ targetPosition.y = targetPosition.y + self:GetHoverHeight()
+ end
+
+ // Different targets can be attacked from different ranges, depending on size
+ local attackDistance = GetEngagementDistance(currentOrder:GetParam())
+
+ local distanceToTarget = (targetPosition - self:GetOrigin()):GetLength()
+ if (distanceToTarget <= attackDistance) and target:GetIsAlive() then
+ self:_OrderMeleeAttack(target)
+ end
+
+ else
+
+ // otherwise move towards attack location and end order when we get there
+ local targetLocation = currentOrder:GetLocation()
+ if self:GetIsFlying() then
+ targetLocation = GetHoverAt(self, targetLocation)
+ end
+
+ self:MoveToTarget(PhysicsMask.AIMovement, targetLocation, moveSpeed, time)
+
+ local distanceToTarget = (currentOrder:GetLocation() - self:GetOrigin()):GetLength()
+ if distanceToTarget < self:GetMixinConstants().kMoveToDistance then
+ self:CompletedCurrentOrder()
+ end
+
+ end
+
+ end
+
+end
+AddFunctionContract(AttackOrderMixin.ProcessAttackOrder, { Arguments = { "Entity", "number", "number", "number" }, Returns = { } })
+
+function AttackOrderMixin:_GetIsTargetValid(target)
+ return target ~= self and target ~= nil
+end
+
+/**
+ * Returns valid taret within attack distance, if any.
+ */
+function AttackOrderMixin:_FindTarget(attackDistance)
+
+ // Find enemy in range
+ local enemyTeamNumber = GetEnemyTeamNumber(self:GetTeamNumber())
+ local potentialTargets = GetEntitiesWithMixinForTeamWithinRange("Live", enemyTeamNumber, self:GetOrigin(), attackDistance)
+
+ local nearestTarget = nil
+ local nearestTargetDistance = 0
+
+ // Get closest target
+ for index, currentTarget in ipairs(potentialTargets) do
+
+ if self:_GetIsTargetValid(currentTarget) then
+
+ local distance = self:GetDistance(currentTarget)
+ if nearestTarget == nil or distance < nearestTargetDistance then
+
+ nearestTarget = currentTarget
+ nearestTargetDistance = distance
+
+ end
+
+ end
+
+ end
+
+ return nearestTarget
+
+end
+
+function AttackOrderMixin:_OrderMeleeAttack(target)
+
+ local meleeAttackInterval = self:AdjustAttackDelay(self:GetMeleeAttackInterval())
+
+ if Shared.GetTime() > (self.timeOfLastAttackOrder + meleeAttackInterval) then
+
+ self:TriggerEffects(string.format("%s_melee_attack", string.lower(self:GetClassName())))
+
+ // Traceline from us to them
+ local trace = Shared.TraceRay(self:GetMeleeAttackOrigin(), target:GetOrigin(), PhysicsMask.AllButPCs, EntityFilterTwo(self, target))
+
+ local direction = target:GetOrigin() - self:GetOrigin()
+ direction:Normalize()
+
+ // Use player or owner (in the case of MACs, Drifters, etc.)
+ local attacker = self:GetOwner()
+ if self:isa("Player") then
+ attacker = self
+ end
+
+ target:TakeDamage(self:GetMeleeAttackDamage(), attacker, self, trace.endPoint, direction)
+
+ // Play hit effects - doer, target, origin, surface
+ TriggerHitEffects(self, target, trace.endPoint, trace.surface, true)
+
+ self.timeOfLastAttackOrder = Shared.GetTime()
+
+ end
+
+end
\ No newline at end of file
diff --git a/ns2/lua/Balance.lua b/ns2/lua/Balance.lua
index ef2b815..6c03249 100644
--- a/ns2/lua/Balance.lua
+++ b/ns2/lua/Balance.lua
@@ -561,9 +561,9 @@ kMatureShiftMaxEnergy = 150
kShadeInitialEnergy = 25 kShadeMaxEnergy = 100
kShadeCloakCost = 25
-kShadePhantasmFadeCost = 25
-kShadePhantasmOnosCost = 50
-kShadePhantasmCost = 75
+kShadePhantomFadeCost = 25
+kShadePhantomOnosCost = 50
+kShadePhantomCost = 75
kMatureShadeMaxEnergy = 150
kEnergyUpdateRate = 0.5
diff --git a/ns2/lua/BalanceMisc.lua b/ns2/lua/BalanceMisc.lua
index b23c4aa..8e65847 100644
--- a/ns2/lua/BalanceMisc.lua
+++ b/ns2/lua/BalanceMisc.lua
@@ -104,4 +104,7 @@ kCystParentRange = 20 // distance from a cyst another cyst (or minicyst) can be
kMiniCystParentRange = 15 // distance from a minicyst a cyst can be placed
// Damage over time that all cysts take when not connected
-kCystUnconnectedDamage = 12
\ No newline at end of file
+kCystUnconnectedDamage = 12
+
+kPhantomEffigyLifetime = 120
+kPhantomLifetime = 10
\ No newline at end of file
diff --git a/ns2/lua/Bot_Player.lua b/ns2/lua/Bot_Player.lua
index 290e32d..f2928ac 100644
--- a/ns2/lua/Bot_Player.lua
+++ b/ns2/lua/Bot_Player.lua
@@ -325,7 +325,7 @@ function BotPlayer:GenerateMove()
if self.pathingEnabled then
- Server.MoveToTarget(PhysicsMask.AIMovement, player, player:GetWaypointGroupName(), orderLocation, 1.5)
+ Server.MoveToTarget(PhysicsMask.AIMovement, player, GetWaypointGroupName(player), orderLocation, 1.5)
if self:GetNumPoints() ~= 0 then
self:MoveToPoint(player:GetCurrentPathPoint(), move)
diff --git a/ns2/lua/CamouflageMixin.lua b/ns2/lua/CamouflageMixin.lua
index a0c84f7..089d46f 100644
--- a/ns2/lua/CamouflageMixin.lua
+++ b/ns2/lua/CamouflageMixin.lua
@@ -84,6 +84,23 @@ function CamouflageMixin:OnUpdate(deltaTime)
self:_UpdateCamouflage()
end
+function CamouflageMixin:OnSynchronized()
+
+ if Client then
+
+ local newHiddenState = self:GetIsCamouflaged()
+ if self.clientCamoed ~= newHiddenState then
+
+ local isEnemy = GetEnemyTeamNumber(self:GetTeamNumber()) == Client.GetLocalPlayer():GetTeamNumber()
+ self:TriggerEffects("client_cloak_changed", {cloaked = newHiddenState, enemy = isEnemy})
+ self.clientCamoed = newHiddenState
+
+ end
+
+ end
+
+end
+
//self.movementModiferState
function CamouflageMixin:GetCamouflageMaxSpeed(walking)
diff --git a/ns2/lua/CloakableMixin.lua b/ns2/lua/CloakableMixin.lua
index c27009c..5da1e15 100644
--- a/ns2/lua/CloakableMixin.lua
+++ b/ns2/lua/CloakableMixin.lua
@@ -107,6 +107,23 @@ function CloakableMixin:OnUpdate(deltaTime)
self:_UpdateCloakState()
end
+function CloakableMixin:OnSynchronized()
+
+ if Client then
+
+ local newHiddenState = self:GetIsCloaked()
+ if self.clientCloaked ~= newHiddenState then
+
+ local isEnemy = GetEnemyTeamNumber(self:GetTeamNumber()) == Client.GetLocalPlayer():GetTeamNumber()
+ self:TriggerEffects("client_cloak_changed", {cloaked = newHiddenState, enemy = isEnemy})
+ self.clientCloaked = newHiddenState
+
+ end
+
+ end
+
+end
+
function CloakableMixin:OnScan()
self:TriggerUncloak()
end
diff --git a/ns2/lua/Cocoon.lua b/ns2/lua/Cocoon.lua
index 0124611..4db5034 100644
--- a/ns2/lua/Cocoon.lua
+++ b/ns2/lua/Cocoon.lua
@@ -8,6 +8,7 @@
//
// ========= For more information, visit us at http://www.unknownworlds.com =====================
Script.Load("lua/Structure.lua")
+Script.Load("lua/RagdollMixin.lua")
class 'Cocoon' (Structure)
@@ -18,6 +19,14 @@ Cocoon.kModelName = PrecacheAsset("models/alien/cocoon/cocoon.model")
Cocoon.kHealth = 200
Cocoon.kArmor = 50
+function Cocoon:OnCreate()
+
+ Structure.OnCreate(self)
+
+ InitMixin(self, RagdollMixin)
+
+end
+
function Cocoon:OnInit()
self:SetModel(Cocoon.kModelName)
diff --git a/ns2/lua/CommandStructure.lua b/ns2/lua/CommandStructure.lua
index ce069c1..066a9aa 100644
--- a/ns2/lua/CommandStructure.lua
+++ b/ns2/lua/CommandStructure.lua
@@ -7,6 +7,7 @@
//
// ========= For more information, visit us at http://www.unknownworlds.com =====================
Script.Load("lua/Structure.lua")
+Script.Load("lua/RagdollMixin.lua")
class 'CommandStructure' (Structure)
CommandStructure.kMapName = "commandstructure"
@@ -15,7 +16,7 @@ if (Server) then
Script.Load("lua/CommandStructure_Server.lua")
end
-local networkVars =
+CommandStructure.networkVars =
{
occupied = "boolean",
commanderId = "entityid",
@@ -25,6 +26,8 @@ function CommandStructure:OnCreate()
Structure.OnCreate(self)
+ InitMixin(self, RagdollMixin)
+
self.occupied = false
self.commanderId = Entity.invalidId
@@ -45,4 +48,4 @@ function CommandStructure:GetEffectParams(tableParams)
end
-Shared.LinkClassToMap("CommandStructure", CommandStructure.kMapName, networkVars)
\ No newline at end of file
+Shared.LinkClassToMap("CommandStructure", CommandStructure.kMapName, CommandStructure.networkVars)
\ No newline at end of file
diff --git a/ns2/lua/CommandStructure_Server.lua b/ns2/lua/CommandStructure_Server.lua
index 3e074fa..4f778f9 100644
--- a/ns2/lua/CommandStructure_Server.lua
+++ b/ns2/lua/CommandStructure_Server.lua
@@ -82,7 +82,7 @@ function CommandStructure:OnResearchComplete(structure, researchId)
if(structure and (structure:GetId() == self:GetId()) and (researchId == self.level1TechId or researchId == self.level2TechId or researchId == self.level3TechId)) then
// Also changes current health and maxHealth
- success = self:Upgrade(researchId)
+ success = self:UpgradeToTechId(researchId)
end
diff --git a/ns2/lua/Commander.lua b/ns2/lua/Commander.lua
index 5c2ed7e..79c7c6f 100644
--- a/ns2/lua/Commander.lua
+++ b/ns2/lua/Commander.lua
@@ -501,10 +501,6 @@ function Commander:GetHostCommandStructure()
return Shared.GetEntity(self.commandStationId)
end
-function Commander:GetCanDoDamage()
- return false
-end
-
function Commander:OverrideCheckvision()
return false
end
diff --git a/ns2/lua/Commander_FocusPanel.lua b/ns2/lua/Commander_FocusPanel.lua
index fc7d476..4f8caac 100644
--- a/ns2/lua/Commander_FocusPanel.lua
+++ b/ns2/lua/Commander_FocusPanel.lua
@@ -78,7 +78,7 @@ function CommanderUI_GetFocusSelectionIcons()
local entity = player:GetRepresentativeSelectedEntity()
- if entity ~= nil and entity:isa("LiveScriptActor") then
+ if entity ~= nil and HasMixin(entity, "Upgradable") then
// Get upgrades
local upgrades = entity:GetUpgrades()
diff --git a/ns2/lua/Commander_Selection.lua b/ns2/lua/Commander_Selection.lua
index 0746499..5dab7d3 100644
--- a/ns2/lua/Commander_Selection.lua
+++ b/ns2/lua/Commander_Selection.lua
@@ -177,8 +177,7 @@ function Commander:MarqueeSelectEntities(pickStartVec, pickEndVec)
local newSelection = {}
- // Add more class names here to allow selection of other entity types
- local potentials = EntityListToTable(Shared.GetEntitiesWithClassname("LiveScriptActor"))
+ local potentials = GetEntitiesWithMixin("Selectable")
self:GetEntitiesBetweenVecs(potentials, pickStartVec, pickEndVec, newSelection)
@@ -523,7 +522,7 @@ function Commander:GetIsEntityValidForSelection(entity)
// Select living things on our team that aren't us
// For now, don't allow even click selection of enemy units or structures
- if ( entity ~= nil and entity:isa("LiveScriptActor") and (entity:GetTeamNumber() == self:GetTeamNumber()) and (entity:GetIsSelectable()) and (entity ~= self) and entity:GetIsAlive() ) or
+ if ( entity ~= nil and HasMixin(entity, "Live") and (entity:GetTeamNumber() == self:GetTeamNumber()) and (HasMixin(entity, "Selectable") and entity:GetIsSelectable()) and (entity ~= self) and entity:GetIsAlive() ) or
// ...and doors
(entity ~= nil and entity:isa("Door")) then
diff --git a/ns2/lua/Commander_SelectionPanel.lua b/ns2/lua/Commander_SelectionPanel.lua
index 84c6f1e..d710167 100644
--- a/ns2/lua/Commander_SelectionPanel.lua
+++ b/ns2/lua/Commander_SelectionPanel.lua
@@ -89,7 +89,7 @@ function CommanderUI_GetPortraitStatus(entityId)
local healthScalar = 1
local entity = Shared.GetEntity(entityId)
- if entity ~= nil and entity:isa("LiveScriptActor") then
+ if entity ~= nil and HasMixin(entity, "Live") then
healthScalar = entity:GetHealthScalar()
end
@@ -231,15 +231,18 @@ function CommanderUI_GetSelectedBargraphs(entityId)
table.insert(t, healthText)
table.insert(t, healthScalar)
- // Build, upgrade or research bar
- local statusText, statusScalar = ent:GetStatusDescription()
+ // Returns text and 0-1 scalar for status bar on commander HUD when selected. Returns nil to display nothing.
+ if ent.GetStatusDescription then
- if statusText ~= nil then
- table.insert(t, statusText)
- table.insert(t, statusScalar)
+ // Build, upgrade or research bar
+ local statusText, statusScalar = ent:GetStatusDescription()
+
+ if statusText ~= nil then
+ table.insert(t, statusText)
+ table.insert(t, statusScalar)
+ end
+
end
-
- //Print("CommanderUI_GetSelectedBargraphs() returning %s", table.tostring(t))
end
@@ -285,16 +288,18 @@ end
* Get custom rightside selection text for a single selection
*/
function CommanderUI_GetSingleSelectionCustomText(entId)
+
local customText = ""
if entId ~= nil then
local ent = Shared.GetEntity(entId)
- if (ent ~= nil) then
+ if ent ~= nil and ent.GetCustomSelectionText then
customText = ent:GetCustomSelectionText()
end
end
return customText
+
end
\ No newline at end of file
diff --git a/ns2/lua/Commander_Server.lua b/ns2/lua/Commander_Server.lua
index 33da468..e203ab8 100644
--- a/ns2/lua/Commander_Server.lua
+++ b/ns2/lua/Commander_Server.lua
@@ -79,8 +79,10 @@ function GetUnattachedEntityWithinRadius(attachclass, position, radius)
end
-// Can't take damage.
-function Commander:GetCanTakeDamage()
+/**
+ * Commanders cannot take damage.
+ */
+function Commander:GetCanTakeDamageOverride()
return false
end
@@ -91,8 +93,11 @@ function Commander:AttemptToResearchOrUpgrade(techNode, force)
local entity = Shared.GetEntity( self.selectedSubGroupEntityIds[1] )
+ // $AS FIXME: We need a better way to do recycling
+ local canResearch = (techNode:GetTechId() == kTechId.Recycle or entity:GetCanResearch())
+
// Don't allow it to be researched while researching
- if( (entity ~= nil and entity:isa("Structure") and entity:GetCanResearch() and techNode:GetCanResearch()) or force) then
+ if( (entity ~= nil and entity:isa("Structure") and canResearch and techNode:GetCanResearch()) or force) then
entity:SetResearching(techNode, self)
entity:OnResearch(techNode:GetTechId())
diff --git a/ns2/lua/Crag.lua b/ns2/lua/Crag.lua
index 5a8e8c0..b9a9b3f 100644
--- a/ns2/lua/Crag.lua
+++ b/ns2/lua/Crag.lua
@@ -13,6 +13,7 @@
// ========= For more information, visit us at http://www.unknownworlds.com =====================
Script.Load("lua/Structure.lua")
Script.Load("lua/InfestationMixin.lua")
+Script.Load("lua/RagdollMixin.lua")
class 'Crag' (Structure)
@@ -34,6 +35,14 @@ Crag.kUmbraRadius = 10
// Umbra blocks 1 out of this many bullet
Crag.kUmbraBulletChance = 2
+function Crag:OnCreate()
+
+ Structure.OnCreate(self)
+
+ InitMixin(self, RagdollMixin)
+
+end
+
function Crag:OnConstructionComplete()
Structure.OnConstructionComplete(self)
@@ -124,7 +133,7 @@ function Crag:OnResearchComplete(structure, researchId)
// Transform into mature crag
if structure and (structure:GetId() == self:GetId()) and (researchId == kTechId.UpgradeCrag) then
- success = self:Upgrade(kTechId.MatureCrag)
+ success = self:UpgradeToTechId(kTechId.MatureCrag)
end
diff --git a/ns2/lua/Cyst.lua b/ns2/lua/Cyst.lua
index 72d0632..c6c9789 100644
--- a/ns2/lua/Cyst.lua
+++ b/ns2/lua/Cyst.lua
@@ -7,6 +7,7 @@
// A cyst controls and spreads infestation
//
// ========= For more information, visit us at http://www.unknownworlds.com =====================
+Script.Load("lua/RagdollMixin.lua")
class 'Cyst' (Structure)
@@ -68,6 +69,14 @@ if Server then
Script.Load("lua/Cyst_Server.lua")
end
+function Cyst:OnCreate()
+
+ Structure.OnCreate(self)
+
+ InitMixin(self, RagdollMixin)
+
+end
+
function Cyst:OnInit()
InitMixin(self, InfestationMixin)
@@ -242,10 +251,6 @@ function Cyst:GetDeployAnimation()
return ""
end
-function Cyst:GetCanDoDamage()
- return false
-end
-
function Cyst:GetEngagementPoint()
// Structure:GetEngagementPoint requires a target attachment point on the model, which Cyst doesn't have right now,
// so override to get rid of the console spam
diff --git a/ns2/lua/Door.lua b/ns2/lua/Door.lua
index 00d8536..cbb60c6 100644
--- a/ns2/lua/Door.lua
+++ b/ns2/lua/Door.lua
@@ -7,7 +7,9 @@
//
// ========= For more information, visit us at http://www.unknownworlds.com =====================
Script.Load("lua/LiveScriptActor.lua")
+Script.Load("lua/UpgradableMixin.lua")
Script.Load("lua/GameEffectsMixin.lua")
+Script.Load("lua/SelectableMixin.lua")
class 'Door' (LiveScriptActor)
@@ -57,14 +59,17 @@ Door.networkVars = {
}
+PrepareClassForMixin(Door, UpgradableMixin)
PrepareClassForMixin(Door, GameEffectsMixin)
function Door:OnCreate()
LiveScriptActor.OnCreate(self)
+ InitMixin(self, UpgradableMixin)
InitMixin(self, GameEffectsMixin)
InitMixin(self, PathingMixin)
+ InitMixin(self, SelectableMixin)
self:SetPathingFlags(Pathing.PolyFlag_NoBuild)
@@ -80,7 +85,7 @@ function Door:OnInit()
self:SetIsVisible(true)
- self:SetPhysicsType(Actor.PhysicsType.Kinematic)
+ self:SetPhysicsType(PhysicsType.Kinematic)
self:SetPhysicsGroup(PhysicsGroup.CommanderUnitGroup)
diff --git a/ns2/lua/Door_Server.lua b/ns2/lua/Door_Server.lua
index 48eb69d..0d1a256 100644
--- a/ns2/lua/Door_Server.lua
+++ b/ns2/lua/Door_Server.lua
@@ -15,7 +15,7 @@ function Door:Reset()
self:SetOrigin(self.savedOrigin)
self:SetAngles(self.savedAngles)
- self:SetPhysicsType(Actor.PhysicsType.Kinematic)
+ self:SetPhysicsType(PhysicsType.Kinematic)
self:SetPhysicsGroup(0)
self:SetState(Door.kState.Closed)
diff --git a/ns2/lua/Drifter.lua b/ns2/lua/Drifter.lua
index 7c39f00..e8d3b4b 100644
--- a/ns2/lua/Drifter.lua
+++ b/ns2/lua/Drifter.lua
@@ -14,8 +14,13 @@ Script.Load("lua/EnergyMixin.lua")
Script.Load("lua/BuildingMixin.lua")
Script.Load("lua/CloakableMixin.lua")
Script.Load("lua/mixins/ControllerMixin.lua")
+Script.Load("lua/RagdollMixin.lua")
+Script.Load("lua/AttackOrderMixin.lua")
+Script.Load("lua/UpgradableMixin.lua")
+Script.Load("lua/PointGiverMixin.lua")
Script.Load("lua/GameEffectsMixin.lua")
Script.Load("lua/FlinchMixin.lua")
+Script.Load("lua/SelectableMixin.lua")
Script.Load("lua/TargetMixin.lua")
Script.Load("lua/LOSMixin.lua")
@@ -55,6 +60,7 @@ Drifter.networkVars = {
PrepareClassForMixin(Drifter, EnergyMixin)
PrepareClassForMixin(Drifter, ControllerMixin)
+PrepareClassForMixin(Drifter, UpgradableMixin)
PrepareClassForMixin(Drifter, GameEffectsMixin)
PrepareClassForMixin(Drifter, FlinchMixin)
PrepareClassForMixin(Drifter, CloakableMixin)
@@ -64,11 +70,20 @@ function Drifter:OnCreate()
LiveScriptActor.OnCreate(self)
InitMixin(self, ControllerMixin)
+ InitMixin(self, RagdollMixin)
+ InitMixin(self, DoorMixin)
+ InitMixin(self, EnergyMixin)
+ InitMixin(self, BuildingMixin)
+ InitMixin(self, CloakableMixin)
+ InitMixin(self, PathingMixin)
InitMixin(self, GameEffectsMixin)
+ InitMixin(self, UpgradableMixin)
InitMixin(self, FlinchMixin)
- InitMixin(self, PathingMixin)
+ InitMixin(self, PointGiverMixin)
+ InitMixin(self, SelectableMixin)
if Server then
+ InitMixin(self, AttackOrderMixin, { kMoveToDistance = Drifter.kMoveToDistance })
InitMixin(self, TargetMixin)
InitMixin(self, LOSMixin)
end
@@ -83,15 +98,10 @@ function Drifter:OnCreate()
end
function Drifter:OnInit()
-
- InitMixin(self, DoorMixin)
- InitMixin(self, EnergyMixin)
- InitMixin(self, BuildingMixin)
- InitMixin(self, CloakableMixin)
self:SetModel(Drifter.kModelName)
- self:SetPhysicsType(Actor.PhysicsType.Kinematic)
+ self:SetPhysicsType(PhysicsType.Kinematic)
LiveScriptActor.OnInit(self)
@@ -148,7 +158,7 @@ function Drifter:GetDeathIconIndex()
return kDeathMessageIcon.Drifter
end
-function Drifter:GetCanTakeDamage()
+function Drifter:GetCanTakeDamageOverride()
return not self.landed
end
@@ -211,12 +221,14 @@ function Drifter:OnOverrideOrder(order)
end
function Drifter:GetPositionForEntity(hive)
+
local angle = NetworkRandom() * math.pi*2
local startPoint = self:GetOrigin() + Vector( math.cos(angle)*Drifter.kStartDistance , Drifter.kHoverHeight, math.sin(angle)*Drifter.kStartDistance )
local direction = Vector(self:GetAngles():GetCoords().zAxis)
- startPoint = self:GetHoverAt(startPoint)
+ startPoint = GetHoverAt(self, startPoint)
return BuildCoords(Vector(0, 1, 0), direction, startPoint)
+
end
function Drifter:ProcessJustSpawned()
@@ -279,6 +291,7 @@ function Drifter:OnThink()
self:ProcessMoveOrder(drifterMoveSpeed)
elseif(currentOrder:GetType() == kTechId.Attack) then
+ // From AttackOrderMixin.
self:ProcessAttackOrder(5, drifterMoveSpeed, Drifter.kMoveThinkInterval)
elseif(currentOrder:GetType() == kTechId.Build) then
@@ -416,7 +429,7 @@ function Drifter:ProcessMoveOrder(moveSpeed)
if (currentOrder ~= nil) then
local isBuild = (currentOrder:GetType() == kTechId.Build)
- local hoverAdjustedLocation = self:GetHoverAt(currentOrder:GetLocation())
+ local hoverAdjustedLocation = GetHoverAt(self, currentOrder:GetLocation())
self:MoveToTarget(PhysicsMask.AIMovement, hoverAdjustedLocation, moveSpeed, Drifter.kMoveThinkInterval)
if(not isBuild and self:IsTargetReached(hoverAdjustedLocation, kEpsilon, true)) then
self:CompletedCurrentOrder()
@@ -438,7 +451,9 @@ function Drifter:OnUpdate(deltaTime)
end
- self:UpdateEnergy(deltaTime)
+ if Server then
+ self:UpdateEnergy(deltaTime)
+ end
self.timeOfLastUpdate = Shared.GetTime()
@@ -591,10 +606,6 @@ function Drifter:PerformParasite()
end
-function Drifter:GetWaypointGroupName()
- return kAirWaypointsGroup
-end
-
function Drifter:GetMeleeAttackDamage()
return kDrifterAttackDamage
end
@@ -603,6 +614,10 @@ function Drifter:GetMeleeAttackInterval()
return kDrifterAttackFireDelay
end
+function Drifter:GetMeleeAttackOrigin()
+ return self:GetOrigin()
+end
+
function Drifter:OnOverrideDoorInteraction(inEntity)
return true, 4
end
diff --git a/ns2/lua/DropPack.lua b/ns2/lua/DropPack.lua
index 80db3b5..f8997c3 100644
--- a/ns2/lua/DropPack.lua
+++ b/ns2/lua/DropPack.lua
@@ -19,7 +19,7 @@ function DropPack:OnCreate ()
ScriptActor.OnCreate(self)
- self:SetPhysicsType(Actor.PhysicsType.DynamicServer)
+ self:SetPhysicsType(PhysicsType.DynamicServer)
self:SetPhysicsGroup(PhysicsGroup.ProjectileGroup)
diff --git a/ns2/lua/Egg.lua b/ns2/lua/Egg.lua
index 27b6b31..a0c76ea 100644
--- a/ns2/lua/Egg.lua
+++ b/ns2/lua/Egg.lua
@@ -10,6 +10,7 @@
Script.Load("lua/Structure.lua")
Script.Load("lua/Onos.lua")
Script.Load("lua/InfestationMixin.lua")
+Script.Load("lua/RagdollMixin.lua")
class 'Egg' (Structure)
PrepareClassForMixin(Egg, InfestationMixin)
@@ -33,8 +34,11 @@ Egg.kArmor = kEggArmor
Egg.kThinkInterval = .5
function Egg:OnCreate()
+
Structure.OnCreate(self)
+ InitMixin(self, RagdollMixin)
+
self:SetModel(Egg.kModelName)
end
diff --git a/ns2/lua/Embryo.lua b/ns2/lua/Embryo.lua
index 647dfa2..a6a442b 100644
--- a/ns2/lua/Embryo.lua
+++ b/ns2/lua/Embryo.lua
@@ -227,8 +227,4 @@ if Server then
end
-function Embryo:GetCanDoDamage()
- return false
-end
-
Shared.LinkClassToMap("Embryo", Embryo.kMapName, Embryo.networkVars)
\ No newline at end of file
diff --git a/ns2/lua/EnergyMixin.lua b/ns2/lua/EnergyMixin.lua
index b965a40..5069c22 100644
--- a/ns2/lua/EnergyMixin.lua
+++ b/ns2/lua/EnergyMixin.lua
@@ -50,8 +50,10 @@ end
AddFunctionContract(EnergyMixin.SetEnergy, { Arguments = { "Entity", "number" }, Returns = { } })
function EnergyMixin:AddEnergy(amount)
+
self.energy = self.energy + amount
self.energy = math.max(math.min(self.energy, self.maxEnergy), 0)
+
end
AddFunctionContract(EnergyMixin.AddEnergy, { Arguments = { "Entity", "number" }, Returns = { } })
@@ -62,6 +64,8 @@ AddFunctionContract(EnergyMixin.GetMaxEnergy, { Arguments = { "Entity" }, Return
function EnergyMixin:UpdateEnergy(timePassed)
+ assert(Server)
+
local scalar = ConditionalValue(self:GetGameEffectMask(kGameEffect.OnFire), kOnFireEnergyRecuperationScalar, 1)
// Increase energy for entities that are affected by energize (not PowerPoints)
diff --git a/ns2/lua/Fade_Server.lua b/ns2/lua/Fade_Server.lua
index a7b7fb4..fc10d10 100644
--- a/ns2/lua/Fade_Server.lua
+++ b/ns2/lua/Fade_Server.lua
@@ -18,8 +18,8 @@ function Fade:InitWeapons()
end
-function Fade:GetCanTakeDamage()
- return Alien.GetCanTakeDamage(self) and not self:GetIsBlinking()
+function Fade:GetCanTakeDamageOverride()
+ return Alien.GetCanTakeDamageOverride(self) and not self:GetIsBlinking()
end
function Fade:OnUpdate(deltaTime)
diff --git a/ns2/lua/FireMixin.lua b/ns2/lua/FireMixin.lua
index adaf14f..fb3d653 100644
--- a/ns2/lua/FireMixin.lua
+++ b/ns2/lua/FireMixin.lua
@@ -10,7 +10,8 @@ FireMixin = { }
FireMixin.type = "Fire"
function FireMixin.__prepareclass(toClass)
- ASSERT(toClass.networkVars ~= nil, "EnergyMixin expects the class to have network fields")
+
+ ASSERT(toClass.networkVars ~= nil, "FireMixin expects the class to have network fields")
local addNetworkFields =
{
@@ -21,24 +22,22 @@ function FireMixin.__prepareclass(toClass)
for k, v in pairs(addNetworkFields) do
toClass.networkVars[k] = v
end
+
end
function FireMixin:__initmixin()
+
self.fireAttackerId = Entity.invalidId
self.fireDoerId = Entity.invalidId
self.stopChance = 0
self.timeLastUpdateStopChance = 0
-end
-
-function FireMixin:OverrideSetFire(attacker, doer)
- if self.OnOverrideSetFire then
- self:OnOverrideSetFire(attacker, doer)
- end
+
end
function FireMixin:SetOnFire(attacker, doer)
- if not self:GetCanBeSetOnFire() then
+
+ if not self:GetCanBeSetOnFire() then
return
end
@@ -47,10 +46,10 @@ function FireMixin:SetOnFire(attacker, doer)
self.fireAttackerId = attacker:GetId()
self.fireDoerId = doer:GetId()
- self:OverrideSetFire(attacker, doer)
end
function FireMixin:ClearFire()
+
self:SetGameEffectMask(kGameEffect.OnFire, false)
self.fireAttackerId = Entity.invalidId
@@ -58,21 +57,25 @@ function FireMixin:ClearFire()
self.stopChance = 0
self.timeLastUpdateStopChance = nil
+
end
-function FireMixin:GetIsOnFire ()
+function FireMixin:GetIsOnFire()
return self:GetGameEffectMask(kGameEffect.OnFire)
end
-function FireMixin:_GetStopChance ()
+function FireMixin:_GetStopChance()
+
if self.timeLastUpdateStopChance == nil or (Shared.GetTime() > self.timeLastUpdateStopChance + 10) then
self.stopChance = self.stopChance + kStopFireProbability
self.timeLastUpdateStopChance = Shared.GetTime()
end
return self.stopChance
+
end
-function FireMixin:GetCanBeSetOnFire ()
+function FireMixin:GetCanBeSetOnFire()
+
if self.OnOverrideCanSetFire then
return self:OnOverrideCanSetFire(attacker, doer)
else
@@ -81,18 +84,88 @@ function FireMixin:GetCanBeSetOnFire ()
end
-function FireMixin:UpdateFire(updateEffectsInterval)
+function FireMixin:OnUpdate(deltaTime)
+
if not self:GetIsOnFire() then
return
end
- // Do damage over time
- self:TakeDamage(kBurnDamagePerSecond * updateEffectsInterval, Shared.GetEntity(self.fireAttackerId), Shared.GetEntity(self.fireDoerId))
+ if Server then
+
+ // Do damage over time
+ self:TakeDamage(kBurnDamagePerSecond * deltaTime, Shared.GetEntity(self.fireAttackerId), Shared.GetEntity(self.fireDoerId))
+
+ // See if we put ourselves out
+ local stopFireChance = deltaTime * self:_GetStopChance()
+ if (NetworkRandom() < stopFireChance) then
+ self:ClearFire()
+ end
+
+ elseif Client then
- // See if we put ourselves out
- local stopFireChance = updateEffectsInterval * self:_GetStopChance()
- if (NetworkRandom() < stopFireChance) then
- self:ClearFire()
+ if self.updateClientSideFireEffects == true then
+ self:_UpdateClientFireEffects()
+ self.updateClientSideFireEffects = false
+ end
+
end
+
end
+function FireMixin:OnSynchronized()
+
+ PROFILE("FireMixin:OnSynchronized")
+
+ if Client then
+ self.updateClientSideFireEffects = true
+ end
+
+end
+
+function FireMixin:OnEntityChange(entityId, newEntityId)
+
+ if entityId == self.fireAttackerId then
+ self.fireAttackerId = newEntityId
+ end
+
+end
+
+function FireMixin:_UpdateClientFireEffects()
+
+ // Play on-fire cinematic every so often if we're on fire
+ if self:GetGameEffectMask(kGameEffect.OnFire) and self:GetIsAlive() and self:GetIsVisible() then
+
+ // If we haven't played effect for a bit
+ local time = Shared.GetTime()
+
+ if not self.timeOfLastFireEffect or (time > (self.timeOfLastFireEffect + .5)) then
+
+ local firstPerson = (Client.GetLocalPlayer() == self)
+ local cinematicName = GetOnFireCinematic(self, firstPerson)
+
+ if firstPerson then
+ local viewModel = self:GetViewModelEntity()
+ if viewModel then
+ Shared.CreateAttachedEffect(self, cinematicName, viewModel, Coords.GetTranslation(Vector(0, 0, 0)), "", true, false)
+ end
+ else
+ Shared.CreateEffect(self, cinematicName, self, self:GetAngles():GetCoords())
+ end
+
+ self.timeOfLastFireEffect = time
+
+ end
+
+ end
+
+end
+
+function FireMixin:OnGameEffectMaskChanged(effect, state)
+
+ if effect == kGameEffect.OnFire and state then
+ self:TriggerEffects("fire_start")
+ elseif effect == kGameEffect.OnFire and not state then
+ self:TriggerEffects("fire_stop")
+ end
+
+end
\ No newline at end of file
diff --git a/ns2/lua/GUIAlienHUD.lua b/ns2/lua/GUIAlienHUD.lua
index 4e416d7..dff2a9b 100644
--- a/ns2/lua/GUIAlienHUD.lua
+++ b/ns2/lua/GUIAlienHUD.lua
@@ -74,6 +74,11 @@ GUIAlienHUD.kInactiveAbilityBarOffset = Vector(-GUIAlienHUD.kInactiveAbilityBarW
GUIAlienHUD.kSelectedAbilityColor = Color(1, 1, 1, 1)
GUIAlienHUD.kUnselectedAbilityColor = Color(0.5, 0.5, 0.5, 1)
+GUIAlienHUD.kPhantomTextFontSize = 24
+GUIAlienHUD.kPhantomProgressBarWidth = 200
+GUIAlienHUD.kPhantomProgressBarHeight = 10
+GUIAlienHUD.kPhantomProgressBarColor = Color(0.0, 0.24313725490196078431372549019608, 0.48235294117647058823529411764706, 0.5)
+
function GUIAlienHUD:Initialize()
// Stores all state related to fading balls.
@@ -164,6 +169,27 @@ function GUIAlienHUD:CreateHealthBall()
self.healthBall:GetBackground():AddChild(self.armorText)
+ // Add bar that goes down over time
+ self.phantomProgressBar = GUIManager:CreateGraphicItem()
+ self.phantomProgressBar:SetSize(Vector(GUIAlienHUD.kPhantomProgressBarWidth, GUIAlienHUD.kPhantomProgressBarHeight, 0))
+ self.phantomProgressBar:SetAnchor(GUIItem.Middle, GUIItem.Bottom)
+ self.phantomProgressBar:SetPosition(Vector(0, -20, 0))
+ self.phantomProgressBar:SetColor(GUIAlienHUD.kPhantomProgressBarColor)
+
+ // Display "Phantom" help text
+ self.phantomText = GUIManager:CreateTextItem()
+ self.phantomText:SetIsVisible(false)
+ self.phantomText:SetFontSize(GUIAlienHUD.kPhantomTextFontSize)
+ self.phantomText:SetFontName(GUIAlienHUD.kTextFontName)
+ self.phantomText:SetPosition(Vector(0, GUIAlienHUD.kHealthTextYOffset, 0))
+ self.phantomText:SetColor(GUIAlienHUD.kFontColor)
+ self.phantomText:SetInheritsParentAlpha(true)
+ self.phantomText:SetAnchor(GUIItem.Middle, GUIItem.Bottom)
+ self.phantomText:SetTextAlignmentX(GUIItem.Align_Center)
+ self.phantomText:SetTextAlignmentY(GUIItem.Align_Center)
+ self.phantomText:SetPosition(Vector(0, -50, 0))
+ self.phantomText:SetText(Locale.ResolveString("ALIEN_HUD_PHANTOM"))
+
end
function GUIAlienHUD:CreateEnergyBall()
@@ -303,6 +329,7 @@ function GUIAlienHUD:Update(deltaTime)
self:UpdateHealthBall(deltaTime)
self:UpdateEnergyBall(deltaTime)
+ self:UpdatePhantom(deltaTime)
end
@@ -346,6 +373,26 @@ function GUIAlienHUD:UpdateEnergyBall(deltaTime)
end
+function GUIAlienHUD:UpdatePhantom(deltaTime)
+
+ local visible = false
+ local player = Client.GetLocalPlayer()
+ local progressWidth = GUIAlienHUD.kPhantomProgressBarWidth
+
+ if player and HasMixin(player, "Phantom") and player:GetIsPhantom() then
+
+ visible = true
+ progressWidth = GUIAlienHUD.kPhantomProgressBarWidth * (player:GetPhantomLifetime() / kPhantomLifetime)
+
+ end
+
+ self.phantomText:SetIsVisible(visible)
+
+ self.phantomProgressBar:SetSize(Vector(progressWidth, GUIAlienHUD.kPhantomProgressBarHeight, 0))
+ self.phantomProgressBar:SetIsVisible(visible)
+
+end
+
function GUIAlienHUD:UpdateAbilities(deltaTime)
local activeHudSlot = 0
diff --git a/ns2/lua/GeneralEffects.lua b/ns2/lua/GeneralEffects.lua
index faab8fe..0239483 100644
--- a/ns2/lua/GeneralEffects.lua
+++ b/ns2/lua/GeneralEffects.lua
@@ -13,6 +13,8 @@ kGeneralEffectData =
onCreateEffects =
{
{parented_sound = "sound/ns2.fev/marine/structures/mac/hover", classname = "MAC", done = true},
+ // TODO:
+ //{sound = "sound/ns2.fev/", classname = "PhantomEffigy", done = true},
},
},
@@ -386,6 +388,25 @@ kGeneralEffectData =
},
},
+
+ phantom_effigy_expire =
+ {
+ effigyExpireEffects =
+ {
+ {sound = "sound/ns2.fev/alien/structures/shade/cloak_end"},
+ {cinematic = "cinematics/marine/clone_structure.cinematic"},
+ },
+ },
+
+ // "enter" into effigy when using it
+ phantom_effigy_start =
+ {
+ effigyStartFX =
+ {
+ {sound = "sound/ns2.fev/alien/structures/shade/cloak_start"},
+ {cinematic = "cinematics/marine/clone_structure.cinematic"},
+ },
+ },
}
diff --git a/ns2/lua/Hydra.lua b/ns2/lua/Hydra.lua
index 8429aed..f716154 100644
--- a/ns2/lua/Hydra.lua
+++ b/ns2/lua/Hydra.lua
@@ -9,6 +9,7 @@
//
// ========= For more information, visit us at http://www.unknownworlds.com =====================
Script.Load("lua/Structure.lua")
+Script.Load("lua/RagdollMixin.lua")
class 'Hydra' (Structure)
@@ -29,6 +30,14 @@ if Server then
Script.Load("lua/HydraSpike.lua")
end
+function Hydra:OnCreate()
+
+ Structure.OnCreate(self)
+
+ InitMixin(self, RagdollMixin)
+
+end
+
function Hydra:GetFov()
return Hydra.kFov
end
@@ -52,7 +61,8 @@ end
function Hydra:GetDeployAnimation()
return ""
end
-function Hydra:GetCanDoDamage()
+
+function Hydra:GetCanGiveDamageOverride()
return true
end
diff --git a/ns2/lua/InfantryPortal.lua b/ns2/lua/InfantryPortal.lua
index a863794..62fc508 100644
--- a/ns2/lua/InfantryPortal.lua
+++ b/ns2/lua/InfantryPortal.lua
@@ -6,6 +6,7 @@
//
// ========= For more information, visit us at http://www.unknownworlds.com =====================
Script.Load("lua/Structure.lua")
+Script.Load("lua/RagdollMixin.lua")
class 'InfantryPortal' (Structure)
@@ -30,6 +31,14 @@ InfantryPortal.kThinkInterval = 0.25
InfantryPortal.kTransponderPointValue = 15
InfantryPortal.kLoginAttachPoint = "keypad"
+function InfantryPortal:OnCreate()
+
+ Structure.OnCreate(self)
+
+ InitMixin(self, RagdollMixin)
+
+end
+
function InfantryPortal:OnInit()
Structure.OnInit(self)
diff --git a/ns2/lua/Infestation.lua b/ns2/lua/Infestation.lua
index cf325ca..dce45fc 100644
--- a/ns2/lua/Infestation.lua
+++ b/ns2/lua/Infestation.lua
@@ -8,6 +8,8 @@
//
// ========= For more information, visit us at http://www.unknownworlds.com =====================
Script.Load("lua/LiveScriptActor.lua")
+Script.Load("lua/UpgradableMixin.lua")
+Script.Load("lua/PointGiverMixin.lua")
Script.Load("lua/GameEffectsMixin.lua")
Script.Load("lua/FlinchMixin.lua")
Script.Load("lua/LOSMixin.lua")
@@ -42,6 +44,7 @@ Infestation.networkVars =
hostAlive = "boolean",
}
+PrepareClassForMixin(Infestation, UpgradableMixin)
PrepareClassForMixin(Infestation, GameEffectsMixin)
PrepareClassForMixin(Infestation, FlinchMixin)
@@ -49,8 +52,10 @@ function Infestation:OnCreate()
LiveScriptActor.OnCreate(self)
+ InitMixin(self, UpgradableMixin)
InitMixin(self, GameEffectsMixin)
InitMixin(self, FlinchMixin)
+ InitMixin(self, PointGiverMixin)
InitMixin(self, PathingMixin)
self.health = Infestation.kInitialHealth
@@ -139,10 +144,6 @@ function Infestation:GetTechId()
return kTechId.Infestation
end
-function Infestation:GetIsSelectable()
- return false
-end
-
function Infestation:OnThink()
PROFILE("Infestation:OnThink")
diff --git a/ns2/lua/LiveMixin.lua b/ns2/lua/LiveMixin.lua
index 7960343..3a2682f 100644
--- a/ns2/lua/LiveMixin.lua
+++ b/ns2/lua/LiveMixin.lua
@@ -11,11 +11,16 @@ Script.Load("lua/BalanceHealth.lua")
LiveMixin = { }
LiveMixin.type = "Live"
-// Whatever uses the LiveMixin needs to implement the following callback functions.
-LiveMixin.expectedCallbacks = {
- GetCanTakeDamage = "Should return false if the object cannot take damage.",
+
+// These may be optionally implemented.
+LiveMixin.optionalCallbacks =
+{
OnTakeDamage = "A callback to alert when the object has taken damage.",
- OnKill = "A callback to alert when the object has been killed." }
+ OnKill = "A callback to alert when the object has been killed.",
+ GetCanTakeDamageOverride = "Should return false if the entity cannot take damage. If this function is not provided it will be assumed that the entity can take damage.",
+ GetCanGiveDamageOverride = "Should return false if the entity cannot give damage to other entities. If this function is not provided it will be assumed that the entity cannot do damage.",
+ GetSendDeathMessageOverride = "Should return false if the entity doesn't send a death message on death."
+}
LiveMixin.kHealth = 100
LiveMixin.kArmor = 0
@@ -48,11 +53,11 @@ function LiveMixin:__initmixin()
self.alive = true
- self.health = LookupTechData(self:GetTechId(), kTechDataMaxHealth, self:GetMixinConstants().kHealth)
+ self.health = LookupTechData(self:GetTechId(), kTechDataMaxHealth, 100)
ASSERT(self.health ~= nil)
self.maxHealth = self.health
- self.armor = LookupTechData(self:GetTechId(), kTechDataMaxArmor, self:GetMixinConstants().kArmor)
+ self.armor = LookupTechData(self:GetTechId(), kTechDataMaxArmor, 0)
ASSERT(self.armor ~= nil)
self.maxArmor = self.armor
@@ -181,7 +186,17 @@ AddFunctionContract(LiveMixin.GetIsAlive, { Arguments = { "Entity" }, Returns =
function LiveMixin:SetIsAlive(state)
- ASSERT(type(state) == "boolean")
+ // It isn't ideal to be destroying the controller here but it is more robust as there
+ // are cases where the entity will be marked as not alive without going through the
+ // TakeDamageServer() function.
+ if self.alive and not state then
+
+ if HasMixin(self, "Controller") then
+ self:DestroyController()
+ end
+
+ end
+
self.alive = state
end
@@ -238,6 +253,8 @@ function LiveMixin:ComputeDamageFromUpgrades(attacker, damage, damageType, time)
end
// Give damage bonus if someone else hit us recently
+ // Note: Game specific things like "Swarm" should not be in here. The "Swarm" upgrade should
+ // apply whatever effect is needed itself.
if attacker and attacker.GetHasUpgrade and attacker:GetHasUpgrade(kTechId.Swarm) then
if self.timeOfLastDamage ~= nil and (time <= (self.timeOfLastDamage + kSwarmInterval)) then
@@ -338,12 +355,15 @@ function LiveMixin:ComputeDamage(attacker, damage, damageType, time)
end
AddFunctionContract(LiveMixin.ComputeDamage, { Arguments = { "Entity", "Entity", "number", "number", { "number", "nil" } }, Returns = { "number", "number", "number" } })
-function LiveMixin:GetLastDamage()
-
- return self.timeOfLastDamage, self.lastDamageAttackerId
+function LiveMixin:GetTimeOfLastDamage()
+ return self.timeOfLastDamage
+end
+AddFunctionContract(LiveMixin.GetTimeOfLastDamage, { Arguments = { "Entity" }, Returns = { { "number", "nil" } } })
+function LiveMixin:GetAttackerIdOfLastDamage()
+ return self.lastDamageAttackerId
end
-AddFunctionContract(LiveMixin.GetLastDamage, { Arguments = { "Entity" }, Returns = { { "number", "nil" }, "number" } })
+AddFunctionContract(LiveMixin.GetAttackerIdOfLastDamage, { Arguments = { "Entity" }, Returns = { "number" } })
function LiveMixin:SetLastDamage(time, attacker)
@@ -355,6 +375,26 @@ function LiveMixin:SetLastDamage(time, attacker)
end
AddFunctionContract(LiveMixin.SetLastDamage, { Arguments = { "Entity", "number", { "Entity", "nil" } }, Returns = { } })
+function LiveMixin:GetCanTakeDamage()
+
+ if self.GetCanTakeDamageOverride then
+ return self:GetCanTakeDamageOverride()
+ end
+ return true
+
+end
+AddFunctionContract(LiveMixin.GetCanTakeDamage, { Arguments = { "Entity" }, Returns = { "boolean" } })
+
+function LiveMixin:GetCanGiveDamage()
+
+ if self.GetCanGiveDamageOverride then
+ return self:GetCanGiveDamageOverride()
+ end
+ return false
+
+end
+AddFunctionContract(LiveMixin.GetCanGiveDamage, { Arguments = { "Entity" }, Returns = { "boolean" } })
+
/**
* Returns true if the damage has killed the entity.
*/
@@ -384,7 +424,7 @@ AddFunctionContract(LiveMixin.TakeDamage, { Arguments = { "Entity", "number", "E
*/
function LiveMixin:TakeDamageClient(damage, attacker, doer, point, direction)
- if self:GetIsAlive() then
+ if self:GetIsAlive() and self.OnTakeDamage then
self:OnTakeDamage(damage, attacker, doer, point)
@@ -398,7 +438,8 @@ AddFunctionContract(LiveMixin.TakeDamageClient, { Arguments = { "Entity", "numbe
function LiveMixin:TakeDamageServer(damage, attacker, doer, point, direction)
- if (self:GetIsAlive() and GetGamerules():CanEntityDoDamageTo(attacker, self)) then
+ local killedFromDamage = false
+ if self:GetIsAlive() and GetGamerules():CanEntityDoDamageTo(attacker, self) then
// Get damage type from source
local damageType = kDamageType.Normal
@@ -427,7 +468,9 @@ function LiveMixin:TakeDamageServer(damage, attacker, doer, point, direction)
if damage > 0 then
- self:OnTakeDamage(damage, attacker, doer, point)
+ if self.OnTakeDamage then
+ self:OnTakeDamage(damage, attacker, doer, point)
+ end
// Remember time we were last hurt for Swarm upgrade
self:SetLastDamage(Shared.GetTime(), attacker)
@@ -442,17 +485,19 @@ function LiveMixin:TakeDamageServer(damage, attacker, doer, point, direction)
// GetDeathIconIndex used to identify the attack type.
Server.SendNetworkMessage(doerPlayer, "GiveDamageIndicator", BuildGiveDamageIndicatorMessage(damage, doer:GetDeathIconIndex(), self:isa("Player"), self:GetTeamNumber()), false)
end
-
+
if (oldHealth > 0 and self:GetHealth() == 0) then
// Do this first to make sure death message is sent
GetGamerules():OnKill(self, damage, attacker, doer, point, direction)
- self:OnKill(damage, attacker, doer, point, direction)
+ if self.OnKill then
+ self:OnKill(damage, attacker, doer, point, direction)
+ end
- self:ProcessFrenzy(attacker, self)
+ self:SetIsAlive(false)
- self.justKilled = true
+ killedFromDamage = true
end
@@ -460,7 +505,7 @@ function LiveMixin:TakeDamageServer(damage, attacker, doer, point, direction)
end
- return (self.justKilled == true)
+ return killedFromDamage
end
AddFunctionContract(LiveMixin.TakeDamageServer, { Arguments = { "Entity", "number", "Entity", "Entity", { "Vector", "nil" }, { "Vector", "nil" } }, Returns = { "boolean" } })
@@ -512,18 +557,27 @@ function LiveMixin:AddHealth(health, playSound, noArmor)
end
AddFunctionContract(LiveMixin.AddHealth, { Arguments = { "Entity", "number", "boolean" }, Returns = { "number" } })
-function LiveMixin:ProcessFrenzy(attacker, targetEntity)
+// This function needs to be tested.
+function LiveMixin:Kill(attacker, doer, point, direction)
+ self:TakeDamage((self:GetMaxHealth() + self:GetMaxArmor()), attacker, doer, nil, nil)
+end
+AddFunctionContract(LiveMixin.Kill, { Arguments = { "Entity", "Entity", "Entity", { "Vector", "nil" }, { "Vector", "nil" } }, Returns = { } })
- // Process Frenzy - give health back according to the amount of extra damage we did
- if attacker and attacker.GetHasUpgrade and attacker:GetHasUpgrade(kTechId.Frenzy) and targetEntity and targetEntity.GetOverkillHealth then
-
- attacker:TriggerEffects("frenzy")
-
- local overkillHealth = targetEntity:GetOverkillHealth()
- local healthToGiveBack = math.max(overkillHealth, kFrenzyMinHealth)
- attacker:AddHealth(healthToGiveBack, false)
-
+// This function needs to be tested.
+function LiveMixin:GetSendDeathMessage()
+
+ if self.GetSendDeathMessageOverride then
+ return self:GetSendDeathMessageOverride()
end
+ return self:GetIsAlive()
end
-AddFunctionContract(LiveMixin.ProcessFrenzy, { Arguments = { "Entity", "Entity", "Entity" }, Returns = { } })
\ No newline at end of file
+AddFunctionContract(LiveMixin.GetSendDeathMessage, { Arguments = { "Entity" }, Returns = { "boolean" } })
+
+/**
+ * Entities using LiveMixin are only selectable when they are alive.
+ */
+function LiveMixin:OnGetIsSelectable(selectableTable)
+ selectableTable.Selectable = selectableTable.Selectable and self:GetIsAlive()
+end
+AddFunctionContract(LiveMixin.OnGetIsSelectable, { Arguments = { "Entity", "table" }, Returns = { } })
\ No newline at end of file
diff --git a/ns2/lua/LiveScriptActor.lua b/ns2/lua/LiveScriptActor.lua
index 5cc08d5..58de584 100644
--- a/ns2/lua/LiveScriptActor.lua
+++ b/ns2/lua/LiveScriptActor.lua
@@ -19,11 +19,6 @@ class 'LiveScriptActor' (ScriptActor)
LiveScriptActor.kMapName = "livescriptactor"
-LiveScriptActor.kHealth = 100
-LiveScriptActor.kArmor = 0
-
-LiveScriptActor.kDefaultPointValue = 10
-
LiveScriptActor.kMoveToDistance = 1
if (Server) then
@@ -34,12 +29,6 @@ end
LiveScriptActor.networkVars =
{
- // Purchased tech (carapace, piercing, etc.). Also includes
- // global and class upgrades we didn't explicitly buy (armor1).
- upgrade1 = "enum kTechId",
- upgrade2 = "enum kTechId",
- upgrade3 = "enum kTechId",
- upgrade4 = "enum kTechId",
// Number of furys that are affecting this entity
furyLevel = string.format("integer (0 to %d)", kMaxStackLevel),
@@ -58,7 +47,7 @@ function LiveScriptActor:OnCreate()
ScriptActor.OnCreate(self)
- InitMixin(self, LiveMixin, { kHealth = LiveScriptActor.kHealth, kArmor = LiveScriptActor.kArmor })
+ InitMixin(self, LiveMixin)
InitMixin(self, OrdersMixin, { kMoveToDistance = LiveScriptActor.kMoveToDistance })
InitMixin(self, FireMixin)
@@ -71,11 +60,6 @@ function LiveScriptActor:OnInit()
self.timeLastUpdate = nil
- self.upgrade1 = kTechId.None
- self.upgrade2 = kTechId.None
- self.upgrade3 = kTechId.None
- self.upgrade4 = kTechId.None
-
self.furyLevel = 0
self.activityEnd = 0
@@ -109,102 +93,16 @@ function LiveScriptActor:GetCanNewActivityStart()
return false
end
-// Used for sentries/hydras to figure out what to attack first
-function LiveScriptActor:GetCanDoDamage()
- return false
-end
-
function LiveScriptActor:GetCanIdle()
return self:GetIsAlive()
end
-function LiveScriptActor:GetHasUpgrade(techId)
- return techId ~= kTechId.None and (techId == self.upgrade1 or techId == self.upgrade2 or techId == self.upgrade3 or techId == self.upgrade4)
-end
-
-function LiveScriptActor:GiveUpgrade(techId)
-
- if not self:GetHasUpgrade(techId) then
-
- if self.upgrade1 == kTechId.None then
-
- self.upgrade1 = techId
- return true
-
- elseif self.upgrade2 == kTechId.None then
-
- self.upgrade2 = techId
- return true
-
- elseif self.upgrade3 == kTechId.None then
-
- self.upgrade3 = techId
- return true
-
- elseif self.upgrade4 == kTechId.None then
-
- self.upgrade4 = techId
- return true
-
- end
-
- Print("%s:GiveUpgrade(%d): Player already has the max of four upgrades.", self:GetClassName())
-
- else
- Print("%s:GiveUpgrade(%d): Player already has tech %s.", self:GetClassName(), techId, GetDisplayNameForTechId(techId))
- end
-
- return false
-
-end
-
-function LiveScriptActor:OnGiveUpgrade(techId)
-end
-
-function LiveScriptActor:GetUpgrades()
- local upgrades = {}
-
- if self.upgrade1 ~= kTechId.None then
- table.insert(upgrades, self.upgrade1)
- end
- if self.upgrade2 ~= kTechId.None then
- table.insert(upgrades, self.upgrade2)
- end
- if self.upgrade3 ~= kTechId.None then
- table.insert(upgrades, self.upgrade3)
- end
- if self.upgrade4 ~= kTechId.None then
- table.insert(upgrades, self.upgrade4)
- end
-
- return upgrades
-end
-
-// Used for flying creatures so they stay at this height off the ground whenever possible
-function LiveScriptActor:GetHoverHeight()
- return 0
-end
-
-// Returns text and 0-1 scalar for status bar on commander HUD when selected. Return nil to display nothing.
-function LiveScriptActor:GetStatusDescription()
- return nil, nil
-end
-
function LiveScriptActor:OnUpdate(deltaTime)
PROFILE("LiveScriptActor:OnUpdate")
ScriptActor.OnUpdate(self, deltaTime)
- // Process outside of OnProcessMove() because animations can't be set there
- if Server then
- self:UpdateJustKilled()
- end
-
- if (self.controller ~= nil and not self:GetIsAlive()) then
- self:DestroyController()
- end
-
// Update expiring stackable game effects
if Server then
@@ -217,30 +115,6 @@ function LiveScriptActor:OnUpdate(deltaTime)
end
-function LiveScriptActor:GetIsSelectable()
- return self:GetIsAlive()
-end
-
-function LiveScriptActor:GetPointValue()
- return LookupTechData(self:GetTechId(), kTechDataPointValue, LiveScriptActor.kDefaultPointValue)
-end
-
-// If the gamerules indicate it's OK an entity to take damage, it calls this. World objects or those without
-// health can return false.
-function LiveScriptActor:GetCanTakeDamage()
- return true
-end
-
-function LiveScriptActor:OnEntityChange(entityId, newEntityId)
-
- ScriptActor.OnEntityChange(self, entityId, newEntityId)
-
- if entityId == self.fireAttackerId then
- self.fireAttackerId = newEntityId
- end
-
-end
-
function LiveScriptActor:GetFuryLevel()
return self.furyLevel
end
@@ -259,8 +133,4 @@ function LiveScriptActor:AdjustFuryFireDelay(inDelay)
end
-function LiveScriptActor:GetSendDeathMessage()
- return self:GetIsAlive()
-end
-
Shared.LinkClassToMap("LiveScriptActor", LiveScriptActor.kMapName, LiveScriptActor.networkVars )
\ No newline at end of file
diff --git a/ns2/lua/LiveScriptActor_Client.lua b/ns2/lua/LiveScriptActor_Client.lua
index 63ca4ab..ed179bc 100644
--- a/ns2/lua/LiveScriptActor_Client.lua
+++ b/ns2/lua/LiveScriptActor_Client.lua
@@ -5,72 +5,4 @@
// Created by: Charlie Cleveland (charlie@unknownworlds.com)
//
//
-// ========= For more information, visit us at http://www.unknownworlds.com =====================
-
-function LiveScriptActor:OnTakeDamage(damage, attacker, doer, point)
-end
-
-function LiveScriptActor:OnKill(damage, attacker, doer, point, direction)
-end
-
-function LiveScriptActor:OnSynchronized()
-
- PROFILE("LiveScriptActor:OnSynchronized")
-
- ScriptActor.OnSynchronized(self)
- self:UpdateEffects()
-
-end
-
-// Display text when selected
-function LiveScriptActor:GetCustomSelectionText()
- return ""
-end
-
-function LiveScriptActor:UpdateEffects()
-
- // Play on-fire cinematic every so often if we're on fire
- if self:GetGameEffectMask(kGameEffect.OnFire) and self:GetIsAlive() and self:GetIsVisible() then
-
- // If we haven't played effect for a bit
- local time = Shared.GetTime()
-
- if not self.timeOfLastFireEffect or (time > (self.timeOfLastFireEffect + .5)) then
-
- local firstPerson = (Client.GetLocalPlayer() == self)
- local cinematicName = GetOnFireCinematic(self, firstPerson)
-
- if firstPerson then
- local viewModel = self:GetViewModelEntity()
- if viewModel then
- Shared.CreateAttachedEffect(self, cinematicName, viewModel, Coords.GetTranslation(Vector(0, 0, 0)), "", true, false)
- end
- else
- Shared.CreateEffect(self, cinematicName, self, self:GetAngles():GetCoords())
- end
-
- self.timeOfLastFireEffect = time
-
- end
-
- end
-
- // If our cloak state changes, all effects to change
- if HasMixin(self, "Cloakable") or HasMixin(self, "Camouflage") then
-
- if self.clientCloaked == nil then
- self.clientCloaked = false
- end
-
- local newHiddenState = (HasMixin(self, "Cloakable") and self:GetIsCloaked()) or (HasMixin(self, "Camouflage") and self:GetIsCamouflaged())
- if self.clientCloaked ~= newHiddenState then
-
- local isEnemy = GetEnemyTeamNumber(self:GetTeamNumber()) == Client.GetLocalPlayer():GetTeamNumber()
- self:TriggerEffects("client_cloak_changed", {cloaked = newHiddenState, enemy = isEnemy})
- self.clientCloaked = newHiddenState
-
- end
-
- end
-
-end
+// ========= For more information, visit us at http://www.unknownworlds.com =====================
\ No newline at end of file
diff --git a/ns2/lua/LiveScriptActor_Server.lua b/ns2/lua/LiveScriptActor_Server.lua
index 42dbab6..0cb3db9 100644
--- a/ns2/lua/LiveScriptActor_Server.lua
+++ b/ns2/lua/LiveScriptActor_Server.lua
@@ -6,394 +6,15 @@
//
// ========= For more information, visit us at http://www.unknownworlds.com =====================
-function LiveScriptActor:CopyDataFrom(player)
-
- self.gameEffectsFlags = player.gameEffectsFlags
-
- table.copy(player.gameEffects, self.gameEffects)
-
- self.timeOfLastDamage = player.timeOfLastDamage
-
- self.furyLevel = player.furyLevel
-
- self.activityEnd = player.activityEnd
-
- self.pathingEnabled = player.pathingEnabled
-
-end
-
function LiveScriptActor:SetPathingEnabled(state)
self.pathingEnabled = state
end
-function LiveScriptActor:Upgrade(newTechId)
-
- if self:GetTechId() ~= newTechId then
-
- // Preserve health and armor scalars but potentially change maxHealth and maxArmor
- local healthScalar = self:GetHealthScalar()
- local armorScalar = self:GetArmorScalar()
-
- self:SetTechId(newTechId)
-
- self:SetMaxHealth(LookupTechData(newTechId, kTechDataMaxHealth, self:GetMaxHealth()))
- self:SetMaxArmor(LookupTechData(newTechId, kTechDataMaxArmor, self:GetMaxArmor()))
-
- self:SetHealth(healthScalar * self:GetMaxHealth())
- self:SetArmor(armorScalar * self:GetMaxArmor())
-
- return true
-
- end
-
- return false
-
-end
-
-function LiveScriptActor:UpdateJustKilled()
-
- if self.justKilled then
-
- // Clear current animation so we know if it was set in TriggerEffects
- self:SetAnimation("", true)
-
- self:TriggerEffects("death")
-
- // Destroy immediately if death animation or ragdoll wasn't triggered (used queued because we're in OnProcessMove)
- local anim = self:GetAnimation()
- if (self:GetPhysicsGroup() == PhysicsGroup.RagdollGroup) or (anim ~= nil and anim ~= "") then
-
- // Set default time to destroy so it's impossible to have things lying around
- self.timeToDestroy = Shared.GetTime() + 4
- self:SetNextThink(.1)
-
- else
- self:SafeDestroy()
- end
-
- self.justKilled = nil
-
- end
-
-end
-
-function LiveScriptActor:GetDamageImpulse(damage, doer, point)
- if damage and doer and point then
- return GetNormalizedVector(doer:GetOrigin() - point) * (damage / 40) * .01
- end
- return nil
-end
-
-function LiveScriptActor:OnTakeDamage(damage, attacker, doer, point)
-
- // Play audio/visual effects when taking damage
- local damageType = kDamageType.Normal
- if doer then
- damageType = doer:GetDamageType()
- end
-
- // Apply directed impulse to physically simulated objects, according to amount of damage
- if (self.physicsModel ~= nil and self.physicsType == Actor.PhysicsType.Dynamic) then
- local damageImpulse = self:GetDamageImpulse(damage, doer, point)
- if damageImpulse then
- self.physicsModel:AddImpulse(point, damageImpulse)
- end
- end
-
-end
-
-function LiveScriptActor:GetTimeOfLastDamage()
- return self.timeOfLastDamage
-end
-
function LiveScriptActor:SetFuryLevel(level)
self.furyLevel = level
end
-function LiveScriptActor:Reset()
-
- ScriptActor.Reset(self)
- self:ResetUpgrades()
- self:ClearOrders()
-
-end
-
-function LiveScriptActor:OnKill(damage, attacker, doer, point, direction)
-
- // Give points to killer
- local pointOwner = attacker
-
- // If the pointOwner is not a player, award it's points to it's owner.
- if pointOwner ~= nil and not pointOwner:isa("Player") then
- pointOwner = pointOwner:GetOwner()
- end
- if(pointOwner ~= nil and pointOwner:isa("Player") and pointOwner:GetTeamNumber() ~= self:GetTeamNumber()) then
- pointOwner:AddScore(self:GetPointValue())
- end
-
- self:SetIsAlive(false)
-
- if point then
- self.deathImpulse = self:GetDamageImpulse(damage, doer, point)
- self.deathPoint = Vector(point)
- end
-
- self:ResetUpgrades()
- self:ClearOrders()
-
- ScriptActor.OnKill(self, damage, attacker, doer, point, direction)
-
-end
-
-function LiveScriptActor:ResetUpgrades()
- self.upgrade1 = kTechId.None
- self.upgrade2 = kTechId.None
- self.upgrade3 = kTechId.None
- self.upgrade4 = kTechId.None
-end
-
-function LiveScriptActor:SetRagdoll(deathTime)
-
- if self:GetPhysicsGroup() ~= PhysicsGroup.RagdollGroup then
-
- self:SetPhysicsType(Actor.PhysicsType.Dynamic)
-
- self:SetPhysicsGroup(PhysicsGroup.RagdollGroup)
-
- // Apply landing blow death impulse to ragdoll (but only if we didn't play death animation)
- if self.deathImpulse and self.deathPoint and self.physicsModel and self.physicsType == Actor.PhysicsType.Dynamic then
-
- self.physicsModel:AddImpulse(self.deathPoint, self.deathImpulse)
- self.deathImpulse = nil
-
- end
-
- if deathTime then
-
- self.timeToDestroy = Shared.GetTime() + deathTime
-
- self:SetNextThink(.1)
-
- end
-
- end
-
-end
-
-function LiveScriptActor:OnThink()
-
- ScriptActor.OnThink(self)
-
- if self.timeToDestroy and (Shared.GetTime() > self.timeToDestroy) then
-
- self:SafeDestroy()
-
- else
- self:SetNextThink(.1)
- end
-
-end
-
-function LiveScriptActor:SafeDestroy()
-
- if bit.bor(self.gameEffectsFlags, kGameEffect.OnFire) then
- self:TriggerEffects("fire_stop")
- end
-
- if(self:GetIsMapEntity()) then
-
- self:SetIsAlive(false)
- self:SetIsVisible(false)
- self:SetNextThink(-1)
- self:SetPhysicsType(Actor.PhysicsType.None)
-
- else
-
- DestroyEntity(self)
-
- end
-
-end
-
-function LiveScriptActor:Kill(attacker, doer, point, direction)
- self:TakeDamage(1000, attacker, doer, nil, nil)
-end
-
// If false, then MoveToTarget() projects entity down to floor
function LiveScriptActor:GetIsFlying()
return false
-end
-
-/**
- * Return the passed in position casted down to the ground.
- */
-function LiveScriptActor:GetGroundAt(position, physicsGroupMask)
-
- local topOffset = self:GetExtents().y
- local startPosition = position + Vector(0, topOffset, 0)
- local endPosition = position - Vector(0, 1000, 0)
-
- local trace = Shared.TraceRay(startPosition, endPosition, physicsGroupMask, EntityFilterOne(self))
-
- // If we didn't hit anything, then use our existing position. This
- // prevents objects from constantly moving downward if they get outside
- // of the bounds of the map.
- if trace.fraction ~= 1 then
- return trace.endPoint
- else
- return position
- end
-
-end
-
-function LiveScriptActor:GetHoverAt(position)
-
- local ground = self:GetGroundAt(position, PhysicsMask.AIMovement)
- local resultY = position.y
- // if we have a hover height, use it to find our minimum height above ground, otherwise use zero
-
- local minHeightAboveGround = 0
- if self.GetHoverHeight then
- minHeightAboveGround = self:GetHoverHeight()
- end
-
- local heightAboveGround = resultY - ground.y
-
- // always snap "up", snap "down" only if not flying
- if heightAboveGround <= minHeightAboveGround or not self:GetIsFlying() then
- resultY = resultY + minHeightAboveGround - heightAboveGround
- end
-
- if resultY ~= position.y then
- return Vector(position.x, resultY, position.z)
- end
-
- return position
-
-end
-
-function LiveScriptActor:GetWaypointGroupName()
- return ConditionalValue(self:GetIsFlying(), kAirWaypointsGroup, kDefaultWaypointGroup)
-end
-
-function LiveScriptActor:MoveToTarget(physicsGroupMask, location, movespeed, time)
- PROFILE("LiveScriptActor:MoveToTarget")
-
- local movement = nil
- local newLocation = self:GetOrigin()
- local now = Shared.GetTime()
- local hasReachedLocation = false//self:IsTargetReached(location, 0.01, true)
-
- local direction = (location - self:GetOrigin()):GetUnit();
- if self.pathingEnabled then
- if not (hasReachedLocation) then
- if not self:IsPathValid(self:GetOrigin(), location) then
- if not (self:BuildPath(self:GetOrigin(), location)) then
- return
- end
- end
-
- if (self:GetCurrentPathPoint() ~= nil and self:GetNumPoints() >= 1) then
- self:RestartPathing(now)
- local point = self:GetNextPoint(time, movespeed)
- if (point ~= nil) then
- newLocation = point
- direction = self:GetPathDirection()
- SetAnglesFromVector(self, direction)
- end
- end
- end
- end
-
- if self:GetIsFlying() then
- newLocation = self:GetHoverAt(newLocation)
- end
-
- self:SetOrigin(newLocation)
- if (self.controller and not self:GetIsFlying()) then
- self:UpdateControllerFromEntity()
- self:PerformMovement(Vector(0, -1000, 0), 1)
- end
-
-end
-
-function LiveScriptActor:PerformAction(techNode, position)
-
- if(techNode:GetTechId() == kTechId.Stop) then
- self:ClearOrders()
- return true
- end
-
- return ScriptActor.PerformAction(self, techNode, position)
-
-end
-
-function LiveScriptActor:OnWeld(entity, elapsedTime)
-end
-
-// Overrideable by children. Called on server only.
-function LiveScriptActor:OnGameEffectMaskChanged(effect, state)
-
- if effect == kGameEffect.OnFire and state then
- self:TriggerEffects("fire_start")
- elseif effect == kGameEffect.OnFire and not state then
- self:TriggerEffects("fire_stop")
- end
-
-end
-
-function LiveScriptActor:GetMeleeAttackDamage()
- return 5
-end
-
-function LiveScriptActor:GetMeleeAttackInterval()
- return .6
-end
-
-function LiveScriptActor:GetMeleeAttackOrigin()
- return self:GetOrigin()
-end
-
-function LiveScriptActor:MeleeAttack(target, time)
-
- local meleeAttackInterval = self:AdjustFuryFireDelay(self:GetMeleeAttackInterval())
-
- if(Shared.GetTime() > (self.timeOfLastAttack + meleeAttackInterval)) then
-
- self:TriggerEffects(string.format("%s_melee_attack", string.lower(self:GetClassName())))
-
- // Traceline from us to them
- local trace = Shared.TraceRay(self:GetMeleeAttackOrigin(), target:GetOrigin(), PhysicsMask.AllButPCs, EntityFilterTwo(self, target))
-
- local direction = target:GetOrigin() - self:GetOrigin()
- direction:Normalize()
-
- // Use player or owner (in the case of MACs, Drifters, etc.)
- local attacker = self:GetOwner()
- if self:isa("Player") then
- attacker = self
- end
-
- target:TakeDamage(self:GetMeleeAttackDamage(), attacker, self, trace.endPoint, direction)
-
- // Play hit effects - doer, target, origin, surface
- TriggerHitEffects(self, target, trace.endPoint, trace.surface, true)
-
- self.timeOfLastAttack = Shared.GetTime()
-
- end
-
-end
-
-// Get target of attack order, if any
-function LiveScriptActor:GetTarget()
- local target = nil
-
- local order = self:GetCurrentOrder()
- if order ~= nil and (order:GetType() == kTechId.Attack or order:GetType() == kTechId.SetTarget) then
- target = Shared.GetEntity(order:GetParam())
- end
-
- return target
-end
-
+end
\ No newline at end of file
diff --git a/ns2/lua/MAC.lua b/ns2/lua/MAC.lua
index 68a573f..28f6ca5 100644
--- a/ns2/lua/MAC.lua
+++ b/ns2/lua/MAC.lua
@@ -13,11 +13,19 @@ Script.Load("lua/DoorMixin.lua")
Script.Load("lua/EnergyMixin.lua")
Script.Load("lua/BuildingMixin.lua")
Script.Load("lua/mixins/ControllerMixin.lua")
+Script.Load("lua/RagdollMixin.lua")
+Script.Load("lua/UpgradableMixin.lua")
+Script.Load("lua/PointGiverMixin.lua")
Script.Load("lua/GameEffectsMixin.lua")
Script.Load("lua/FlinchMixin.lua")
+Script.Load("lua/SelectableMixin.lua")
Script.Load("lua/TargetMixin.lua")
Script.Load("lua/LOSMixin.lua")
+if Server then
+ Script.Load("lua/AttackOrderMixin.lua")
+end
+
class 'MAC' (LiveScriptActor)
MAC.kMapName = "mac"
@@ -71,6 +79,7 @@ MAC.networkVars = {}
PrepareClassForMixin(MAC, EnergyMixin)
PrepareClassForMixin(MAC, ControllerMixin)
+PrepareClassForMixin(MAC, UpgradableMixin)
PrepareClassForMixin(MAC, GameEffectsMixin)
PrepareClassForMixin(MAC, FlinchMixin)
@@ -79,11 +88,19 @@ function MAC:OnCreate()
LiveScriptActor.OnCreate(self)
InitMixin(self, ControllerMixin)
+ InitMixin(self, RagdollMixin)
+ InitMixin(self, DoorMixin)
+ InitMixin(self, EnergyMixin)
+ InitMixin(self, BuildingMixin)
+ InitMixin(self, UpgradableMixin)
InitMixin(self, GameEffectsMixin)
InitMixin(self, FlinchMixin)
+ InitMixin(self, PointGiverMixin)
InitMixin(self, PathingMixin)
+ InitMixin(self, SelectableMixin)
if Server then
+ InitMixin(self, AttackOrderMixin, { kMoveToDistance = MAC.kMoveToDistance })
InitMixin(self, TargetMixin)
InitMixin(self, LOSMixin)
end
@@ -100,16 +117,12 @@ function MAC:OnCreate()
end
function MAC:OnInit()
-
- InitMixin(self, DoorMixin)
- InitMixin(self, EnergyMixin )
- InitMixin(self, BuildingMixin )
LiveScriptActor.OnInit(self)
self:SetModel(MAC.kModelName)
- self:SetPhysicsType(Actor.PhysicsType.Kinematic)
+ self:SetPhysicsType(PhysicsType.Kinematic)
if(Server) then
@@ -334,7 +347,7 @@ function MAC:ProcessWeldOrder(deltaTime)
else
// otherwise move towards it
- local hoverAdjustedLocation = self:GetHoverAt(target:GetEngagementPoint())
+ local hoverAdjustedLocation = GetHoverAt(self, target:GetEngagementPoint())
self:MoveToTarget(PhysicsMask.AIMovement, hoverAdjustedLocation, self:GetMoveSpeed(), deltaTime)
self.timeOfLastWeld = time
end
@@ -372,7 +385,7 @@ end
function MAC:ProcessMove(deltaTime)
local currentOrder = self:GetCurrentOrder()
- local hoverAdjustedLocation = self:GetHoverAt(currentOrder:GetLocation())
+ local hoverAdjustedLocation = GetHoverAt(self, currentOrder:GetLocation())
self:MoveToTarget(PhysicsMask.AIMovement, hoverAdjustedLocation, self:GetMoveSpeed(), deltaTime)
if(self:IsTargetReached(currentOrder:GetLocation(), kEpsilon)) then
@@ -381,13 +394,16 @@ function MAC:ProcessMove(deltaTime)
else
self:TriggerEffects("mac_move")
end
+
end
function MAC:PlayChatSound(soundName)
+
if self.timeOfLastChatterSound == 0 or (Shared.GetTime() > self.timeOfLastChatterSound + 2) then
self:PlaySound(soundName)
self.timeOfLastChatterSound = Shared.GetTime()
end
+
end
// Look for other MACs and Drifters to greet as we fly by
@@ -486,7 +502,7 @@ function MAC:ProcessBuildConstruct(deltaTime)
end
end
else
- local hoverAdjustedLocation = self:GetHoverAt(currentOrder:GetLocation())
+ local hoverAdjustedLocation = GetHoverAt(self, currentOrder:GetLocation())
self:MoveToTarget(PhysicsMask.AIMovement, hoverAdjustedLocation, self:GetMoveSpeed(), deltaTime)
end
end
@@ -517,7 +533,8 @@ function MAC:UpdateOrders(deltaTime)
if(currentOrder:GetType() == kTechId.Move) then
self:ProcessMove(deltaTime)
self:UpdateGreetings()
- elseif(currentOrder:GetType() == kTechId.Attack) then
+ elseif(currentOrder:GetType() == kTechId.Attack) then
+ // From AttackOrderMixin.
self:ProcessAttackOrder(1, GetDevScalar(MAC.kMoveSpeed, 8), deltaTime)
elseif(currentOrder:GetType() == kTechId.Weld) then
setNextThink = self:ProcessWeldOrder(deltaTime)
@@ -541,7 +558,9 @@ function MAC:OnUpdate(deltaTime)
self:UpdateControllerFromEntity()
- self:UpdateEnergy(deltaTime)
+ if Server then
+ self:UpdateEnergy(deltaTime)
+ end
end
@@ -610,10 +629,6 @@ function MAC:GetTechButtons(techId)
end
-function MAC:GetWaypointGroupName()
- return kAirWaypointsGroup
-end
-
function MAC:OnOverrideDoorInteraction(inEntity)
// MACs will not open the door if they are currently
// welding it shut
diff --git a/ns2/lua/NS2ConsoleCommands_Server.lua b/ns2/lua/NS2ConsoleCommands_Server.lua
index fef2014..9f02ec4 100644
--- a/ns2/lua/NS2ConsoleCommands_Server.lua
+++ b/ns2/lua/NS2ConsoleCommands_Server.lua
@@ -776,6 +776,25 @@ function OnCommandTarget(client, cmd)
end
end
+function OnCommandPhantom(client, cmd)
+
+ if client ~= nil and Shared.GetCheatsEnabled() then
+
+ if type(cmd) == "string" then
+
+ local player = client:GetControllingPlayer()
+ local origin = player:GetOrigin()
+
+ StartPhantomMode(player, cmd, origin)
+
+ else
+ Print("Must pass map name.")
+ end
+
+ end
+
+end
+
// GC commands
Event.Hook("Console_changegcsettingserver", OnCommandChangeGCSettingServer)
@@ -847,3 +866,4 @@ Event.Hook("Console_setgameeffect", OnCommandSetGameEffect)
Event.Hook("Console_eject", OnCommandEject)
Event.Hook("Console_cyst", OnCommandCyst)
Event.Hook("Console_target", OnCommandTarget)
+Event.Hook("Console_phantom", OnCommandPhantom)
\ No newline at end of file
diff --git a/ns2/lua/NS2Utility.lua b/ns2/lua/NS2Utility.lua
index 8bc4aa3..54b6ae5 100644
--- a/ns2/lua/NS2Utility.lua
+++ b/ns2/lua/NS2Utility.lua
@@ -182,13 +182,12 @@ function CheckBuildEntityRequirements(techId, position, player, ignoreEntity)
local techNode = techTree:GetTechNode(techId)
local attachClass = LookupTechData(techId, kStructureAttachClass)
- // Build tech can't be built on top of LiveScriptActors
+ // Build tech can't be built on top of non-attachment entities.
if techNode and techNode:GetIsBuild() then
local trace = Shared.TraceBox(GetExtents(techId), position + Vector(0, 1, 0), position - Vector(0, 3, 0), PhysicsMask.AllButPCs, EntityFilterOne(ignoreEntity))
- // $AS - We special case Drop Packs as they are not LiveScriptActors but you should not be
- // able to build on top of them
+ // $AS - We special case Drop Packs you should not be able to build on top of them.
if trace.entity and trace.entity:GetHasPathingFlag(kPathingFlags.UnBuildable) then
legalBuild = false
end
@@ -323,11 +322,63 @@ function GetIsBuildLegal(techId, position, snapRadius, player, ignoreEntity)
end
+/**
+ * Return the passed in position casted down to the ground.
+ */
+function GetGroundAt(entity, position, physicsGroupMask)
+
+ local topOffset = entity:GetExtents().y
+ local startPosition = position + Vector(0, topOffset, 0)
+ local endPosition = position - Vector(0, 1000, 0)
+
+ local trace = Shared.TraceRay(startPosition, endPosition, physicsGroupMask, EntityFilterOne(entity))
+
+ // If we didn't hit anything, then use our existing position. This
+ // prevents objects from constantly moving downward if they get outside
+ // of the bounds of the map.
+ if trace.fraction ~= 1 then
+ return trace.endPoint
+ else
+ return position
+ end
+
+end
+
+function GetHoverAt(entity, position)
+
+ local ground = GetGroundAt(entity, position, PhysicsMask.AIMovement)
+ local resultY = position.y
+ // if we have a hover height, use it to find our minimum height above ground, otherwise use zero
+
+ local minHeightAboveGround = 0
+ if entity.GetHoverHeight then
+ minHeightAboveGround = entity:GetHoverHeight()
+ end
+
+ local heightAboveGround = resultY - ground.y
+
+ // always snap "up", snap "down" only if not flying
+ if heightAboveGround <= minHeightAboveGround or not entity:GetIsFlying() then
+ resultY = resultY + minHeightAboveGround - heightAboveGround
+ end
+
+ if resultY ~= position.y then
+ return Vector(position.x, resultY, position.z)
+ end
+
+ return position
+
+end
+
+function GetWaypointGroupName(entity)
+ return ConditionalValue(entity:GetIsFlying(), kAirWaypointsGroup, kDefaultWaypointGroup)
+end
+
function GetTriggerEntity(position, teamNumber)
local triggerEntity = nil
local minDist = nil
- local ents = GetEntitiesForTeamWithinRange("LiveScriptActor", teamNumber, position, .5)
+ local ents = GetEntitiesWithMixinForTeamWithinRange("Live", teamNumber, position, .5)
for index, ent in ipairs(ents) do
@@ -399,7 +450,7 @@ end
function GetBlockedByUmbra(entity)
- if entity ~= nil and entity:isa("LiveScriptActor") then
+ if entity ~= nil and HasMixin(entity, "GameEffects") then
if entity:GetGameEffectMask(kGameEffect.InUmbra) and (NetworkRandomInt(1, Crag.kUmbraBulletChance, "GetBlockedByUmbra") == 1) then
return true
@@ -1070,7 +1121,7 @@ function ResetLights()
end
end
-// Pulled out into separate function so phantasms can use it too
+// Pulled out into separate function so phantoms can use it too
function SetPlayerPoseParameters(player, viewAngles, velocity, maxSpeed, maxBackwardSpeedScalar, crouchAmount)
local pitch = -Math.Wrap( Math.Degrees(viewAngles.pitch), -180, 180 )
@@ -1286,7 +1337,7 @@ function GetActivationTarget(teamNumber, position)
local nearestTarget = nil
local nearestDist = nil
- local targets = GetEntitiesForTeamWithinRange("LiveScriptActor", teamNumber, position, 2)
+ local targets = GetEntitiesWithMixinForTeamWithinRange("Live", teamNumber, position, 2)
for index, target in ipairs(targets) do
if target:GetIsVisible() and not target:isa("Infestation") then
@@ -1612,7 +1663,7 @@ function CanEntityDoDamageTo(attacker, target, cheats, devMode, friendlyFire)
end
// Phantom damage sources can't damage players
- if attacker ~= nil and HasMixin(attacker, "Phantom") and attacker:GetIsPhantom() then
+ if attacker ~= nil and HasMixin(attacker, "Phantom") and attacker:GetIsPhantom() and (attacker ~= target) then
return false
end
@@ -1651,4 +1702,5 @@ function CanEntityDoDamageTo(attacker, target, cheats, devMode, friendlyFire)
// Allow damage of own stuff when testing
return teamsOK
-end
\ No newline at end of file
+end
+
diff --git a/ns2/lua/Observatory.lua b/ns2/lua/Observatory.lua
index 209ff17..584160d 100644
--- a/ns2/lua/Observatory.lua
+++ b/ns2/lua/Observatory.lua
@@ -6,6 +6,7 @@
//
// ========= For more information, visit us at http://www.unknownworlds.com =====================
Script.Load("lua/Structure.lua")
+Script.Load("lua/RagdollMixin.lua")
class 'Observatory' (Structure)
@@ -18,6 +19,14 @@ Observatory.kScanSound = PrecacheAsset("sound/ns2.fev/marine/structures/observat
Observatory.kDistressBeaconTime = kDistressBeaconTime
Observatory.kDistressBeaconRange = kDistressBeaconRange
+function Observatory:OnCreate()
+
+ Structure.OnCreate(self)
+
+ InitMixin(self, RagdollMixin)
+
+end
+
function Observatory:OnInit()
self:SetModel(Observatory.kModelName)
diff --git a/ns2/lua/OrderSelfMixin.lua b/ns2/lua/OrderSelfMixin.lua
index 1160117..eb511a0 100644
--- a/ns2/lua/OrderSelfMixin.lua
+++ b/ns2/lua/OrderSelfMixin.lua
@@ -62,7 +62,7 @@ function OrderSelfMixin:_FindDefendOrder(structuresNearby)
local closestStructureDist = Math.infinity
for i, structure in ipairs(structuresNearby) do
local structureDist = (structure:GetOrigin() - self:GetOrigin()):GetLengthSquared()
- local lastTimeDamageTaken, attackerId = structure:GetLastDamage()
+ local lastTimeDamageTaken = structure:GetTimeOfLastDamage()
if lastTimeDamageTaken and lastTimeDamageTaken > 0 and ((Shared.GetTime() - lastTimeDamageTaken) < kTimeToDefendSinceTakingDamage) and (structureDist < closestStructureDist) then
closestStructure = structure
closestStructureDist = structureDist
diff --git a/ns2/lua/OrdersMixin.lua b/ns2/lua/OrdersMixin.lua
index 3b052b8..0be0a3e 100644
--- a/ns2/lua/OrdersMixin.lua
+++ b/ns2/lua/OrdersMixin.lua
@@ -11,6 +11,12 @@ Script.Load("lua/FunctionContracts.lua")
OrdersMixin = { }
OrdersMixin.type = "Orders"
+OrdersMixin.expectedCallbacks =
+{
+ // GetExtents() is required by GetGroundAt().
+ GetExtents = "Returns a Vector describing the extents of this Entity."
+}
+
local kTimeSinceDamageDefendComplete = 10
local kDefendCompleteDistance = 5
@@ -130,6 +136,16 @@ function OrdersMixin:ClearOrders()
end
AddFunctionContract(OrdersMixin.ClearOrders, { Arguments = { "Entity" }, Returns = { } })
+function OrdersMixin:Reset()
+ self:ClearOrders()
+end
+AddFunctionContract(OrdersMixin.Reset, { Arguments = { "Entity" }, Returns = { } })
+
+function OrdersMixin:OnKill()
+ self:ClearOrders()
+end
+AddFunctionContract(OrdersMixin.OnKill, { Arguments = { "Entity" }, Returns = { } })
+
function OrdersMixin:_DestroyOrders()
// Allow ents to hook destruction of current order.
@@ -186,12 +202,12 @@ function OrdersMixin:_SetOrder(order, clearExisting, insertFirst)
// Always snap the location of the order to the ground.
local location = order:GetLocation()
- if self.GetGroundAt then
- location = self:GetGroundAt(location, PhysicsMask.AIMovement)
+ if location then
+ location = GetGroundAt(self, location, PhysicsMask.AIMovement)
+ order:SetLocation(location)
end
- order:SetLocation(location)
- if(insertFirst) then
+ if insertFirst then
table.insert(self.orders, 1, order:GetId())
else
table.insert(self.orders, order:GetId())
@@ -294,79 +310,6 @@ function OrdersMixin:ProcessRallyOrder(originatingEntity)
end
AddFunctionContract(OrdersMixin.ProcessRallyOrder, { Arguments = { "Entity", "Entity" }, Returns = { } })
-// This is an "attack-move" from RTS. Attack the entity specified in our current attack order, if any.
-// Otherwise, move to the location specified in the attack order and attack anything along the way.
-function OrdersMixin:ProcessAttackOrder(targetSearchDistance, moveSpeed, time)
-
- // If we have a target, attack it
- local currentOrder = self:GetCurrentOrder()
- if(currentOrder ~= nil) then
-
- local target = Shared.GetEntity(currentOrder:GetParam())
-
- if target then
-
- // How do you kill that which has no life?
- if not HasMixin(target, "Live") or not target:GetIsAlive() then
-
- self:CompletedCurrentOrder()
-
- else
-
- local targetLocation = target:GetEngagementPoint()
- if self:GetIsFlying() then
- targetLocation = self:GetHoverAt(targetLocation)
- end
-
- self:MoveToTarget(PhysicsMask.AIMovement, targetLocation, moveSpeed, time)
-
- end
-
- else
-
- // Check for a nearby target. If not found, move towards destination.
- target = self:FindTarget(targetSearchDistance)
-
- end
-
- if target then
-
- // If we are close enough to target, attack it
- local targetPosition = Vector(target:GetOrigin())
- targetPosition.y = targetPosition.y + self:GetHoverHeight()
-
- // Different targets can be attacked from different ranges, depending on size
- local attackDistance = GetEngagementDistance(currentOrder:GetParam())
-
- local distanceToTarget = (targetPosition - self:GetOrigin()):GetLength()
- if (distanceToTarget <= attackDistance) and target:GetIsAlive() then
-
- self:MeleeAttack(target, time)
-
-
- end
-
- else
-
- // otherwise move towards attack location and end order when we get there
- local targetLocation = currentOrder:GetLocation()
- if self:GetIsFlying() then
- targetLocation = self:GetHoverAt(targetLocation)
- end
- self:MoveToTarget(PhysicsMask.AIMovement, targetLocation, moveSpeed, time)
-
- local distanceToTarget = (currentOrder:GetLocation() - self:GetOrigin()):GetLength()
- if(distanceToTarget < self:GetMixinConstants().kMoveToDistance) then
- self:CompletedCurrentOrder()
- end
-
- end
-
- end
-
-end
-AddFunctionContract(OrdersMixin.ProcessAttackOrder, { Arguments = { "Entity", "number", "number", "number" }, Returns = { } })
-
function OrdersMixin:UpdateOrder()
local currentOrder = self:GetCurrentOrder()
@@ -420,7 +363,7 @@ function OrdersMixin:UpdateOrder()
self:ClearOrders()
- elseif HasMixin(orderTarget, "Live") and (Shared.GetTime() - orderTarget:GetLastDamage()) > kTimeSinceDamageDefendComplete then
+ elseif HasMixin(orderTarget, "Live") and (Shared.GetTime() - orderTarget:GetTimeOfLastDamage()) > kTimeSinceDamageDefendComplete then
// Only complete if self is close enough to the target.
if (self:GetOrigin() - orderTarget:GetOrigin()):GetLengthSquared() < (kDefendCompleteDistance * kDefendCompleteDistance) then
@@ -437,4 +380,53 @@ function OrdersMixin:UpdateOrder()
end
end
-AddFunctionContract(OrdersMixin.UpdateOrder, { Arguments = { "Entity" }, Returns = { } })
\ No newline at end of file
+AddFunctionContract(OrdersMixin.UpdateOrder, { Arguments = { "Entity" }, Returns = { } })
+
+// Note: This needs to be tested.
+// Get target of attack order, if any.
+function OrdersMixin:GetTarget()
+
+ local target = nil
+
+ local order = self:GetCurrentOrder()
+ if order ~= nil and (order:GetType() == kTechId.Attack or order:GetType() == kTechId.SetTarget) then
+ target = Shared.GetEntity(order:GetParam())
+ end
+
+ return target
+
+end
+
+// Note: This needs to be tested.
+function OrdersMixin:PerformAction(techNode, position)
+
+ local results = { self:OnPerformAction(techNode, position) }
+
+ // Return true if any of the callbacks returned true.
+ for i, result in pairs(results) do
+
+ if result then
+ return true
+ end
+
+ end
+
+ return false
+
+end
+
+/**
+ * Other mixins can implement this function to handle more specific actions.
+ * Called when tech tree action is performed on the entity.
+ * Return true if legal and action handled. Position passed if applicable.
+ */
+function OrdersMixin:OnPerformAction(techNode, position)
+
+ if techNode:GetTechId() == kTechId.Stop then
+ self:ClearOrders()
+ return true
+ end
+
+ return false
+
+end
\ No newline at end of file
diff --git a/ns2/lua/PathingMixin.lua b/ns2/lua/PathingMixin.lua
index 891a708..e95253a 100644
--- a/ns2/lua/PathingMixin.lua
+++ b/ns2/lua/PathingMixin.lua
@@ -72,9 +72,9 @@ local function GeneratePath(src, dst)
Pathing.GetPathPoints(src, dst, points)
// HACKS
- if (#(points) > 0) then
+ /* if (#(points) > 0) then
table.insert( points, #(points) - 1, dst )
- end
+ end */
if (#(points) ~= 0 ) then
SplitPathPoints( points, 0.5 )
@@ -377,4 +377,50 @@ function PathingMixin:ClearPathingFlags(flags)
if (extents ~= nil) then
Pathing.ClearPolyFlags(position, extents, flags)
end
+
+end
+
+/**
+ * This is the bread and butter of PathingMixin.
+ */
+function PathingMixin:MoveToTarget(physicsGroupMask, location, movespeed, time)
+
+ PROFILE("PathingMixin:MoveToTarget")
+
+ local movement = nil
+ local newLocation = self:GetOrigin()
+ local now = Shared.GetTime()
+ local hasReachedLocation = false//self:IsTargetReached(location, 0.01, true)
+
+ local direction = (location - self:GetOrigin()):GetUnit();
+ if self.pathingEnabled then
+ if not (hasReachedLocation) then
+ if not self:IsPathValid(self:GetOrigin(), location) then
+ if not (self:BuildPath(self:GetOrigin(), location)) then
+ return
+ end
+ end
+
+ if (self:GetCurrentPathPoint() ~= nil and self:GetNumPoints() >= 1) then
+ self:RestartPathing(now)
+ local point = self:GetNextPoint(time, movespeed)
+ if (point ~= nil) then
+ newLocation = point
+ direction = self:GetPathDirection()
+ SetAnglesFromVector(self, direction)
+ end
+ end
+ end
+ end
+
+ if self:GetIsFlying() then
+ newLocation = GetHoverAt(self, newLocation)
+ end
+
+ self:SetOrigin(newLocation)
+ if (self.controller and not self:GetIsFlying()) then
+ self:UpdateControllerFromEntity()
+ self:PerformMovement(Vector(0, -1000, 0), 1)
+ end
+
end
\ No newline at end of file
diff --git a/ns2/lua/PhantomEffigy.lua b/ns2/lua/PhantomEffigy.lua
new file mode 100644
index 0000000..1412931
--- /dev/null
+++ b/ns2/lua/PhantomEffigy.lua
@@ -0,0 +1,71 @@
+// ======= Copyright © 2003-2011, Unknown Worlds Entertainment, Inc. All rights reserved. =======
+//
+// lua\PhantomEffigy.lua
+//
+// Created by: Charlie Cleveland (charlie@unknownworlds.com)
+//
+// Additive entity that is created by Alien Commander via the Shade. Looks like an alien life
+// form, which can be +used by friendly players to morph into a PhantomMixin version of that
+// lifeform. Invisible to enemies, dissipates if unused after a couple minutes.
+//
+// ========= For more information, visit us at http://www.unknownworlds.com =====================
+Script.Load("lua/ScriptActor.lua")
+
+class 'PhantomEffigy' (ScriptActor)
+
+PhantomEffigy.kMapName = "phantom"
+PhantomEffigy.kLifetime = kPhantomEffigyLifetime
+
+PhantomEffigy.kNetworkVars = {}
+
+function PhantomEffigy:OnInit()
+
+ ScriptActor.OnInit(self)
+
+ self.createTime = Shared.GetTime()
+
+end
+
+if Server then
+function PhantomEffigy:OnUpdate(deltaTime)
+
+ ScriptActor.OnUpdate(self, deltaTime)
+
+ if Shared.GetTime() > (self.createTime + PhantomEffigy.kLifetime) then
+
+ self:TriggerEffects("phantom_effigy_expire")
+
+ DestroyEntity(self)
+
+ end
+
+end
+end
+
+function PhantomEffigy:GetCanBeUsed(player)
+ // Make sure effigy hasn't been destroyed this frame already. Phantoms can't use effigies of course.
+ return player and (player:GetTeamNumber() == self:GetTeamNumber()) and (self:GetId() ~= Entity.invalidId) and (not HasMixin(player, "Phantom") or not player:GetIsPhantom())
+end
+
+function PhantomEffigy:GetEffigyMorphToMapName()
+ if self:GetTechId() == kTechId.ShadePhantomOnos then
+ return Onos.kMapName
+ end
+ return Fade.kMapName
+end
+
+function PhantomEffigy:OnUse(player, elapsedTime, useAttachPoint, usePoint)
+
+ if Server then
+
+ StartPhantomMode(player, self:GetEffigyMorphToMapName(), self:GetOrigin())
+
+ DestroyEntity(self)
+
+ // TODO: Animate their camera towards viewpoint
+
+ end
+
+end
+
+Shared.LinkClassToMap("PhantomEffigy", PhantomEffigy.kMapName, PhantomEffigy.kNetworkVars)
diff --git a/ns2/lua/PhantomMixin.lua b/ns2/lua/PhantomMixin.lua
index 0086e32..9df9387 100644
--- a/ns2/lua/PhantomMixin.lua
+++ b/ns2/lua/PhantomMixin.lua
@@ -2,6 +2,12 @@
//
// lua\PhantomMixin.lua
//
+// Manages a "phantom" version of a player or structure. The alien commander can use the Shade
+// to allow players to become phantom versions of other aliens. These can be killed but can't
+// do damage. Phantom structures can also be created, which look and act like real versions of
+// the structures, but without being functional in any way (don't contribute to tech tree, don't
+// have abilities, etc.).
+//
// Created by: Charlie Cleveland (charlie@unknownworlds.com)
//
// ========= For more information, visit us at http://www.unknownworlds.com =====================
@@ -13,7 +19,9 @@ PhantomMixin.type = "Phantom"
PhantomMixin.expectedCallbacks =
{
- GetTechId = "Used for?"
+ GetTechId = "Used for?",
+ GetId = "",
+ OnKill = "",
}
function PhantomMixin.__prepareclass(toClass)
@@ -23,7 +31,7 @@ function PhantomMixin.__prepareclass(toClass)
local addNetworkFields =
{
// 0 when not active, > 0 when object is a phantom
- lifetime = "float",
+ phantomLifetime = "float",
expired = "boolean",
}
@@ -34,40 +42,167 @@ function PhantomMixin.__prepareclass(toClass)
end
function PhantomMixin:__initmixin()
- self.lifetime = 0
+ self.phantomLifetime = 0
self.expired = false
+ self.hostPlayerId = Entity.invalidId
+ self.phantomPlayerId = Entity.invalidId
end
// Set duration after which we will expire
-function PhantomMixin:SetLifetime(lifetime)
+function PhantomMixin:SetPhantomLifetime(lifetime)
ASSERT(type(lifetime) == "number")
ASSERT(lifetime >= 0)
- self.lifetime = lifetime
+ self.phantomLifetime = lifetime
self.expired = false
end
+function PhantomMixin:GetPhantomLifetime()
+ return self.phantomLifetime
+end
+
function PhantomMixin:GetIsExpired()
return self.expired
end
function PhantomMixin:OnUpdate(deltaTime)
- self.lifetime = math.max(self.lifetime - deltaTime, 0)
+ if self:GetIsPhantom() then
- if not self.expired and self.lifetime == 0 then
- self.expired = true
+ self.phantomLifetime = math.max(self.phantomLifetime - deltaTime, 0)
+
+ if not self.expired and self.phantomLifetime == 0 then
+
+ self:OnKill(0, nil, nil, nil, nil)
+ self.expired = true
+
+ end
+
end
end
function PhantomMixin:GetIsPhantom()
- return (self.lifetime > 0)
+ return (self.phantomLifetime > 0)
+end
+
+function PhantomMixin:SetHostPlayer(player)
+ self.hostPlayerId = player:GetId()
+end
+
+function PhantomMixin:SetPhantomPlayer(newPlayer)
+ self.phantomPlayerId = newPlayer:GetId()
+end
+
+function PhantomMixin:OnKill(damage, attacker, doer, point, direction)
+
+ if self:GetIsPhantom() then
+
+ self:RestoreToHostPlayer()
+
+ else
+
+ self:KillPhantom(damage, attacker, doer, point, direction)
+
+ end
+
end
+function PhantomMixin:KillPhantom(damage, attacker, doer, point, direction)
+ // Destroy phantom also, in the same way we are destroyed
+ if self.phantomPlayerId ~= Entity.invalidId then
+
+ local phantomPlayer = Shared.GetEntity(self.phantomPlayerId)
+ if phantomPlayer ~= nil and phantomPlayer:isa("Player") then
+
+ // Set before calling OnKill() to avoid recursive issues when calling OnKill() again
+ self.phantomPlayerId = Entity.invalidId
+
+ phantomPlayer:OnKill(damage, attacker, doer, point, direction)
+
+ return true
+
+ end
+
+ end
+
+ return false
+
+end
+function PhantomMixin:RestoreToHostPlayer()
+ if self:GetIsPhantom() and (self.hostPlayerId ~= Entity.invalidId) then
+
+ local hostPlayer = Shared.GetEntity(self.hostPlayerId)
+ if hostPlayer ~= nil and hostPlayer:isa("Player") then
+
+ local playerId = self:GetId()
+ local killedPlayer = Shared.GetEntity(playerId)
+
+ ASSERT(killedPlayer ~= nil)
+ ASSERT(killedPlayer:isa("Player"))
+
+ // Player now controls original again
+ local owner = Server.GetOwner(killedPlayer)
+ ASSERT(owner ~= nil)
+ hostPlayer:SetControllingPlayer(owner)
+
+ // Notify others of the change
+ killedPlayer:SendEntityChanged(hostPlayer:GetId())
+
+ // No longer in phantom mode
+ self.phantomLifetime = 0
+ self.hostPlayerId = Entity.invalidId
+
+ return true
+
+ else
+ Print("PhantomMixin:OnKill(): Couldn't find host player (%s)", ToString(self.hostPlayerId))
+ end
+ end
+
+ return false
+
+end
+
+function PhantomMixin:GetPhantom()
+ if self.phantomPlayerId ~= Entity.invalidId then
+ return Shared.GetEntity(self.phantomPlayerId)
+ end
+ return nil
+end
+
+function PhantomMixin:GetHost()
+ if self.hostPlayerId ~= Entity.invalidId then
+ return Shared.GetEntity(self.hostPlayerId)
+ end
+ return nil
+end
+
+function StartPhantomMode(player, mapName, origin)
+
+ // Transform them into the alien type represented by this effigy
+ local owner = Server.GetOwner(player)
+ local newPlayer = CreateEntity(mapName, origin, player:GetTeamNumber())
+ newPlayer:SetControllingPlayer(owner)
+ newPlayer:SetName(player:GetName())
+
+ // Notify others of the change
+ player:SendEntityChanged(newPlayer:GetId())
+
+ // Set their PhantomMixin() active
+ ASSERT(HasMixin(newPlayer, "Phantom"))
+ newPlayer:SetPhantomLifetime(kPhantomLifetime)
+ player:SetPhantomPlayer(newPlayer)
+ newPlayer:SetHostPlayer(player)
+
+ player:SetScoreboardChanged(true)
+ newPlayer:SetScoreboardChanged(true)
+
+ newPlayer:TriggerEffects("phantom_effigy_start")
+end
\ No newline at end of file
diff --git a/ns2/lua/PhaseGate.lua b/ns2/lua/PhaseGate.lua
index 99fefa4..0f0f8ba 100644
--- a/ns2/lua/PhaseGate.lua
+++ b/ns2/lua/PhaseGate.lua
@@ -6,6 +6,7 @@
//
// ========= For more information, visit us at http://www.unknownworlds.com =====================
Script.Load("lua/Structure.lua")
+Script.Load("lua/RagdollMixin.lua")
// Transform angles, view angles and velocity from srcCoords to destCoords (when going through phase gate)
local function TransformPlayerCoordsForPhaseGate(player, srcCoords, destCoords)
@@ -40,6 +41,14 @@ PhaseGate.networkVars =
destLocationId = "entityid",
}
+function PhaseGate:OnCreate()
+
+ Structure.OnCreate(self)
+
+ InitMixin(self, RagdollMixin)
+
+end
+
function PhaseGate:OnInit()
Structure.OnInit(self)
diff --git a/ns2/lua/PhysicsGroups.lua b/ns2/lua/PhysicsGroups.lua
index aaa0654..ddbc7ed 100644
--- a/ns2/lua/PhysicsGroups.lua
+++ b/ns2/lua/PhysicsGroups.lua
@@ -102,4 +102,10 @@ PhysicsMask = enum
OnlyInfestation = CreateGroupsAllowedMask(PhysicsGroup.InfestationGroup)
}
-
+PhysicsType = enum
+ {
+ 'None', // No physics representation.
+ 'Dynamic', // Bones are driven by physics simulation (client-side only)
+ 'DynamicServer', // Bones are driven by physics simulation (synced with server)
+ 'Kinematic' // Physics model is updated by animation
+ }
\ No newline at end of file
diff --git a/ns2/lua/Player.lua b/ns2/lua/Player.lua
index 6aef65d..73fea1b 100644
--- a/ns2/lua/Player.lua
+++ b/ns2/lua/Player.lua
@@ -17,8 +17,13 @@ Script.Load("lua/TooltipMixin.lua")
Script.Load("lua/WeaponOwnerMixin.lua")
Script.Load("lua/DoorMixin.lua")
Script.Load("lua/mixins/ControllerMixin.lua")
+Script.Load("lua/ScoringMixin.lua")
+Script.Load("lua/RagdollMixin.lua")
+Script.Load("lua/UpgradableMixin.lua")
+Script.Load("lua/PointGiverMixin.lua")
Script.Load("lua/GameEffectsMixin.lua")
Script.Load("lua/FlinchMixin.lua")
+Script.Load("lua/SelectableMixin.lua")
Script.Load("lua/TargetMixin.lua")
Script.Load("lua/LOSMixin.lua")
@@ -227,6 +232,7 @@ Player.networkVars =
}
PrepareClassForMixin(Player, ControllerMixin)
+PrepareClassForMixin(Player, UpgradableMixin)
PrepareClassForMixin(Player, GameEffectsMixin)
PrepareClassForMixin(Player, FlinchMixin)
@@ -235,8 +241,13 @@ function Player:OnCreate()
LiveScriptActor.OnCreate(self)
InitMixin(self, ControllerMixin)
+ InitMixin(self, ScoringMixin, { kMaxScore = kMaxScore })
+ InitMixin(self, RagdollMixin)
+ InitMixin(self, UpgradableMixin)
InitMixin(self, GameEffectsMixin)
InitMixin(self, FlinchMixin)
+ InitMixin(self, PointGiverMixin)
+ InitMixin(self, SelectableMixin)
if Server then
InitMixin(self, TargetMixin)
InitMixin(self, LOSMixin)
@@ -277,7 +288,6 @@ function Player:OnCreate()
self.timeLastSayingsAction = 0
self.reticleTarget = false
self.timeTargetHit = 0
- self.score = 0
self.kills = 0
self.deaths = 0
@@ -983,7 +993,7 @@ function Player:GetIsPlaying()
return self.gameStarted and (self:GetTeamNumber() == kTeam1Index or self:GetTeamNumber() == kTeam2Index)
end
-function Player:GetCanTakeDamage()
+function Player:GetCanTakeDamageOverride()
local teamNumber = self:GetTeamNumber()
return (teamNumber == kTeam1Index or teamNumber == kTeam2Index)
end
@@ -1121,9 +1131,6 @@ function Player:GetMoveDirection(moveVelocity)
end
-function Player:UpdateEnergy(input)
-end
-
function Player:EndUse(deltaTime)
if not self:GetIsUsing() then
return
@@ -2339,10 +2346,6 @@ function Player:GetCanBeUsed(player)
return false
end
-function Player:GetScore()
- return self.score
-end
-
function Player:GetScoreboardChanged()
return self.scoreboardChanged
end
@@ -2459,7 +2462,7 @@ function Player:GetPlayerStatusDesc()
return ""
end
-function Player:GetCanDoDamage()
+function Player:GetCanGiveDamageOverride()
return true
end
diff --git a/ns2/lua/Player_Server.lua b/ns2/lua/Player_Server.lua
index 9109168..51407c6 100644
--- a/ns2/lua/Player_Server.lua
+++ b/ns2/lua/Player_Server.lua
@@ -152,20 +152,20 @@ end
function Player:OnTakeDamage(damage, attacker, doer, point)
- LiveScriptActor.OnTakeDamage(self, damage, attacker, doer, point)
-
if self:GetTeamType() == kAlienTeamType then
self:GetTeam():TriggerAlert(kTechId.AlienAlertLifeformUnderAttack, self)
end
// Play damage indicator for player
if point ~= nil then
+
local damageOrigin = doer:GetOrigin()
local doerParent = doer:GetParent()
if doerParent then
damageOrigin = doerParent:GetOrigin()
end
Server.SendNetworkMessage(self, "TakeDamageIndicator", BuildTakeDamageIndicatorMessage(damageOrigin, damage), true)
+
end
end
@@ -199,22 +199,16 @@ end
*/
function Player:OnKill(damage, killer, doer, point, direction)
+ // Determine the killer's player name.
local killerName = nil
+ if killer ~= nil and not killer:isa("Player") then
- local pointOwner = killer
- // If the pointOwner is not a player, award it's points to it's owner.
- if pointOwner ~= nil and not pointOwner:isa("Player") then
- pointOwner = pointOwner:GetOwner()
- end
- if(pointOwner and pointOwner:isa("Player") and pointOwner ~= self and pointOwner:GetTeamNumber() == GetEnemyTeamNumber(self:GetTeamNumber())) then
-
- killerName = pointOwner:GetName()
- pointOwner:AddKill()
-
- local resAwarded = pointOwner:AwardResForKill(self)
- pointOwner:AddScore(self:GetPointValue(), resAwarded)
+ local realKiller = killer:GetOwner()
+ if realKiller and realKiller:isa("Player") then
+ killerName = realKiller:GetName()
+ end
- end
+ end
// Save death to server log
if(killer == self) then
@@ -238,14 +232,6 @@ function Player:OnKill(damage, killer, doer, point, direction)
self:AddDeaths()
- // Don't allow us to do anything
- self:SetIsAlive(false)
-
- self:ResetUpgrades()
-
- // On fire, in umbra, etc.
- self:ClearGameEffects()
-
// Fade out screen
self.timeOfDeath = Shared.GetTime()
@@ -256,7 +242,7 @@ function Player:OnKill(damage, killer, doer, point, direction)
self:RemoveChildren()
// Create a rag doll
- self:SetPhysicsType(Actor.PhysicsType.Dynamic)
+ self:SetPhysicsType(PhysicsType.Dynamic)
self:SetPhysicsGroup(PhysicsGroup.RagdollGroup)
// Set next think to 0 to disable
@@ -350,11 +336,15 @@ function Player:OnUpdate(deltaTime)
if ((self.timeOfDeath ~= nil) and (time - self.timeOfDeath > kFadeToBlackTime)) then
- // Destroy the existing player and create a spectator in their place.
- local spectator = self:Replace(self:GetDeathMapName())
+ // Destroy the existing player and create a spectator in their place (but only if it has an owner, ie not a body left behind by Phantom use)
+ local owner = Server.GetOwner(self)
+ if owner then
- // Queue up the spectator for respawn.
- spectator:GetTeam():PutPlayerInRespawnQueue(spectator, Shared.GetTime())
+ // Queue up the spectator for respawn.
+ local spectator = self:Replace(self:GetDeathMapName())
+ spectator:GetTeam():PutPlayerInRespawnQueue(spectator, Shared.GetTime())
+
+ end
end
@@ -404,8 +394,13 @@ end
function Player:CopyPlayerDataFrom(player)
- LiveScriptActor.CopyDataFrom(self, player)
-
+ // This is stuff from the former LiveScriptActor.
+ self.gameEffectsFlags = player.gameEffectsFlags
+ table.copy(player.gameEffects, self.gameEffects)
+ self.timeOfLastDamage = player.timeOfLastDamage
+ self.furyLevel = player.furyLevel
+ self.activityEnd = player.activityEnd
+
// ScriptActor and Actor fields
self:SetAngles(player:GetAngles())
self:SetOrigin(Vector(player:GetOrigin()))
@@ -549,7 +544,7 @@ function Player:Replace(mapName, newTeamNumber, preserveChildren)
player:RemoveChildren()
- local childEntities = GetChildEntities(self)
+ local childEntities = GetChildEntities(self, "Weapon")
for index, entity in ipairs(childEntities) do
entity:SetParent(player)
@@ -744,7 +739,7 @@ function Player:RemoveChildren()
self.activeWeaponIndex = 0
// Loop through all children and delete them.
- local childEntities = GetChildEntities(self, "Actor")
+ local childEntities = GetChildEntities(self)
for index, entity in ipairs(childEntities) do
entity:SetParent(nil)
DestroyEntity(entity)
@@ -775,22 +770,6 @@ function Player:GetViewModelBlendTime()
return .1
end
-function Player:GetScore()
- return self.score
-end
-
-function Player:AddScore(points, res)
-
- // Tell client to display cool effect
- if(points ~= nil and points ~= 0) then
- local displayRes = ConditionalValue(type(res) == "number", res, 0)
- Server.SendCommand(self, string.format("points %s %s", tostring(points), tostring(displayRes)))
- self.score = Clamp(self.score + points, 0, kMaxScore)
- self:SetScoreboardChanged(true)
- end
-
-end
-
function Player:GetKills()
return self.kills
end
@@ -902,7 +881,7 @@ function Player:UpdateOrderWaypoint()
if(currentOrder ~= nil) then
local targetLoc = Vector(currentOrder:GetLocation())
- self.nextOrderWaypoint = Server.GetNextWaypoint(PhysicsMask.AIMovement, self, self:GetWaypointGroupName(), targetLoc)
+ self.nextOrderWaypoint = Server.GetNextWaypoint(PhysicsMask.AIMovement, self, GetWaypointGroupName(self), targetLoc)
self.finalWaypoint = Vector(targetLoc)
self.nextOrderWaypointActive = true
self.waypointType = currentOrder:GetType()
diff --git a/ns2/lua/PlayingTeam.lua b/ns2/lua/PlayingTeam.lua
index b978ef7..127a030 100644
--- a/ns2/lua/PlayingTeam.lua
+++ b/ns2/lua/PlayingTeam.lua
@@ -911,7 +911,8 @@ function PlayingTeam:UpdateGameEffects(timePassed)
if self.timeSinceLastGameEffectUpdate >= PlayingTeam.kUpdateGameEffectsInterval then
- // Friendly entities that alien structures can affect
+ // Friendly entities that this team's structures can affect. Any entity on this team with
+ // the GameEffects Mixin.
local teamEntities = GetEntitiesWithMixinForTeam("GameEffects", self:GetTeamNumber())
local enemyPlayers = GetEntitiesForTeam("Player", GetEnemyTeamNumber(self:GetTeamNumber()))
@@ -924,19 +925,6 @@ function PlayingTeam:UpdateGameEffects(timePassed)
end
function PlayingTeam:UpdateTeamSpecificGameEffects(teamEntities, enemyPlayers)
-
- local catchFireEntities = {}
-
- for index, entity in ipairs(teamEntities) do
- if HasMixin(entity, "Fire") then
- entity:UpdateFire(PlayingTeam.kUpdateGameEffectsInterval)
- end
- end
-
- for index, catchFireEntity in ipairs(catchFireEntities) do
- catchFireEntity:SetGameEffectMask(kGameEffect.OnFire, true)
- end
-
end
function PlayingTeam:VoteToEjectCommander(votingPlayer, targetCommander)
diff --git a/ns2/lua/PointGiverMixin.lua b/ns2/lua/PointGiverMixin.lua
new file mode 100644
index 0000000..590c604
--- /dev/null
+++ b/ns2/lua/PointGiverMixin.lua
@@ -0,0 +1,58 @@
+// ======= Copyright © 2003-2011, Unknown Worlds Entertainment, Inc. All rights reserved. =======
+//
+// lua\PointGiverMixin.lua
+//
+// Created by: Brian Cronin (brianc@unknownworlds.com)
+//
+// ========= For more information, visit us at http://www.unknownworlds.com =====================
+
+Script.Load("lua/FunctionContracts.lua")
+
+/**
+ * PointGiverMixin handles awarding points on kills and other events.
+ */
+PointGiverMixin = { }
+PointGiverMixin.type = "PointGiver"
+
+local kDefaultPointValue = 10
+
+PointGiverMixin.expectedCallbacks =
+{
+ GetTeamNumber = "Returns the team number this PointGiver is on.",
+ GetTechId = "Returns the tech Id of this PointGiver."
+}
+
+function PointGiverMixin:__initmixin()
+end
+
+function PointGiverMixin:GetPointValue()
+ return LookupTechData(self:GetTechId(), kTechDataPointValue, kDefaultPointValue)
+end
+
+/**
+ * If a AwardResForKill() function is defined on the attacker then it will be called
+ * passing in self to check how many resources should be awarded for killing self.
+ */
+function PointGiverMixin:OnKill(damage, attacker, doer, point, direction)
+
+ // Give points to killer.
+ local pointOwner = attacker
+
+ // If the pointOwner is not a player, award it's points to it's owner.
+ if pointOwner ~= nil and not HasMixin(pointOwner, "Scoring") then
+ pointOwner = pointOwner:GetOwner()
+ end
+
+ // Points not awarded for entities on the same team.
+ if pointOwner ~= nil and HasMixin(pointOwner, "Scoring") and pointOwner:GetTeamNumber() ~= self:GetTeamNumber() then
+
+ pointOwner:AddKill()
+ local resAwarded = 0
+ if pointOwner.AwardResForKill then
+ resAwarded = pointOwner:AwardResForKill(self)
+ end
+ pointOwner:AddScore(self:GetPointValue(), resAwarded)
+
+ end
+
+end
\ No newline at end of file
diff --git a/ns2/lua/PowerPack.lua b/ns2/lua/PowerPack.lua
index 6a6b8ba..e6061e2 100644
--- a/ns2/lua/PowerPack.lua
+++ b/ns2/lua/PowerPack.lua
@@ -8,6 +8,7 @@
//
// ========= For more information, visit us at http://www.unknownworlds.com =====================
Script.Load("lua/Structure.lua")
+Script.Load("lua/RagdollMixin.lua")
class 'PowerPack' (Structure)
@@ -21,6 +22,14 @@ if Server then
Script.Load("lua/PowerPack_Server.lua")
end
+function PowerPack:OnCreate()
+
+ Structure.OnCreate(self)
+
+ InitMixin(self, RagdollMixin)
+
+end
+
function PowerPack:OnInit()
self:SetModel(PowerPack.kModelName)
diff --git a/ns2/lua/PowerPack_Server.lua b/ns2/lua/PowerPack_Server.lua
index dfa7865..72da1f4 100644
--- a/ns2/lua/PowerPack_Server.lua
+++ b/ns2/lua/PowerPack_Server.lua
@@ -26,13 +26,6 @@ function PowerPack:OnDestroy()
end
-function PowerPack:SafeDestroy()
- self:SetIsAlive(false)
- self:SetNextThink(-1)
-
- Structure.SafeDestroy(self)
-end
-
function PowerPack:UpdateNearbyPowerState()
// Trigger event to update power for nearby structures
diff --git a/ns2/lua/PowerPoint.lua b/ns2/lua/PowerPoint.lua
index 54ea48a..29e8869 100644
--- a/ns2/lua/PowerPoint.lua
+++ b/ns2/lua/PowerPoint.lua
@@ -104,7 +104,7 @@ function PowerPoint:Reset()
end
-function PowerPoint:GetCanTakeDamage()
+function PowerPoint:GetCanTakeDamageOverride()
return self.powered
end
diff --git a/ns2/lua/PowerPoint_Server.lua b/ns2/lua/PowerPoint_Server.lua
index dfdb8ca..f69ab96 100644
--- a/ns2/lua/PowerPoint_Server.lua
+++ b/ns2/lua/PowerPoint_Server.lua
@@ -42,15 +42,6 @@ function PowerPoint:OnKill(damage, attacker, doer, point, direction)
end
-// Power should never be destroyed or ragdoll or they'll be destroyed for good
-function PowerPoint:SafeDestroy()
- self:SetIsAlive(false)
- self:SetNextThink(-1)
-end
-
-function PowerPoint:SetRagdoll(deathTime)
-end
-
function PowerPoint:OnLoad()
Structure.OnLoad(self)
@@ -269,6 +260,6 @@ function PowerPoint:UpdatePoweredStructures()
end
-function PowerPoint:GetSendDeathMessage()
+function PowerPoint:GetSendDeathMessageOverride()
return self.powered
-end
+end
\ No newline at end of file
diff --git a/ns2/lua/PropDynamic.lua b/ns2/lua/PropDynamic.lua
index 1255626..7bc33eb 100644
--- a/ns2/lua/PropDynamic.lua
+++ b/ns2/lua/PropDynamic.lua
@@ -30,10 +30,10 @@ if (Server) then
end
if self.dynamic then
- self:SetPhysicsType(Actor.PhysicsType.Dynamic)
+ self:SetPhysicsType(PhysicsType.Dynamic)
self:SetPhysicsGroup(PhysicsGroup.RagdollGroup)
else
- self:SetPhysicsType(Actor.PhysicsType.Kinematic)
+ self:SetPhysicsType(PhysicsType.Kinematic)
end
// Don't collide when commanding if not full alpha
diff --git a/ns2/lua/PrototypeLab.lua b/ns2/lua/PrototypeLab.lua
index 2ff07f6..d3fd999 100644
--- a/ns2/lua/PrototypeLab.lua
+++ b/ns2/lua/PrototypeLab.lua
@@ -6,6 +6,7 @@
//
// ========= For more information, visit us at http://www.unknownworlds.com =====================
Script.Load("lua/Structure.lua")
+Script.Load("lua/RagdollMixin.lua")
class 'PrototypeLab' (Structure)
@@ -13,6 +14,14 @@ PrototypeLab.kMapName = "prototypelab"
PrototypeLab.kModelName = PrecacheAsset("models/marine/prototype_module/prototype_module.model")
+function PrototypeLab:OnCreate()
+
+ Structure.OnCreate(self)
+
+ InitMixin(self, RagdollMixin)
+
+end
+
function PrototypeLab:OnInit()
self:SetModel(PrototypeLab.kModelName)
diff --git a/ns2/lua/Ragdoll.lua b/ns2/lua/Ragdoll.lua
index c81d4e7..36bf2b4 100644
--- a/ns2/lua/Ragdoll.lua
+++ b/ns2/lua/Ragdoll.lua
@@ -17,7 +17,7 @@ function Ragdoll:OnInit()
ScriptActor.OnInit(self)
- self:SetPhysicsType(Actor.PhysicsType.Dynamic)
+ self:SetPhysicsType(PhysicsType.Dynamic)
self:SetPhysicsGroup(PhysicsGroup.RagdollGroup)
if(Server) then
diff --git a/ns2/lua/RagdollMixin.lua b/ns2/lua/RagdollMixin.lua
new file mode 100644
index 0000000..faffd60
--- /dev/null
+++ b/ns2/lua/RagdollMixin.lua
@@ -0,0 +1,172 @@
+// ======= Copyright © 2003-2011, Unknown Worlds Entertainment, Inc. All rights reserved. =======
+//
+// lua\RagdollMixin.lua
+//
+// Created by: Brian Cronin (brianc@unknownworlds.com)
+//
+// ========= For more information, visit us at http://www.unknownworlds.com =====================
+
+Script.Load("lua/FunctionContracts.lua")
+
+local function GetDamageImpulse(damage, doer, point)
+
+ if damage and doer and point then
+ return GetNormalizedVector(doer:GetOrigin() - point) * (damage / 40) * .01
+ end
+ return nil
+
+end
+
+RagdollMixin = { }
+RagdollMixin.type = "Ragdoll"
+
+RagdollMixin.expectedMixins =
+{
+ Live = "Needed for SetIsAlive()."
+}
+
+RagdollMixin.expectedCallbacks =
+{
+ SetPhysicsType = "Sets the physics to the passed in type.",
+ GetPhysicsType = "Returns the physics type, dynamic, kinematic, etc.",
+ SetPhysicsGroup = "Sets the physics group to the passed in value.",
+ GetPhysicsGroup = "",
+ GetPhysicsModel = "Returns the physics model.",
+ SetAnimation = "",
+ TriggerEffects = ""
+}
+
+function RagdollMixin:__initmixin()
+end
+
+function RagdollMixin:OnTakeDamage(damage, attacker, doer, point)
+
+ // Apply directed impulse to physically simulated objects, according to amount of damage.
+ if self:GetPhysicsModel() ~= nil and self:GetPhysicsType() == PhysicsType.Dynamic then
+
+ local damageImpulse = GetDamageImpulse(damage, doer, point)
+ if damageImpulse then
+ self:GetPhysicsModel():AddImpulse(point, damageImpulse)
+ end
+
+ end
+
+end
+AddFunctionContract(RagdollMixin.OnTakeDamage, { Arguments = { "Entity", "number", "Entity", { "Entity", "nil" }, "Vector" }, Returns = { } })
+
+function RagdollMixin:OnKill(damage, attacker, doer, point, direction)
+
+ self.justKilled = true
+ if point then
+ self.deathImpulse = GetDamageImpulse(damage, doer, point)
+ self.deathPoint = Vector(point)
+ end
+
+end
+AddFunctionContract(RagdollMixin.OnKill, { Arguments = { "Entity", "number", "Entity", { "Entity", "nil" }, "Vector" }, Returns = { } })
+
+function RagdollMixin:SetRagdoll(deathTime)
+
+ if self:GetPhysicsGroup() ~= PhysicsGroup.RagdollGroup then
+
+ self:SetPhysicsType(PhysicsType.Dynamic)
+
+ self:SetPhysicsGroup(PhysicsGroup.RagdollGroup)
+
+ // Apply landing blow death impulse to ragdoll (but only if we didn't play death animation).
+ if self.deathImpulse and self.deathPoint and self:GetPhysicsModel() and self:GetPhysicsType() == PhysicsType.Dynamic then
+
+ self:GetPhysicsModel():AddImpulse(self.deathPoint, self.deathImpulse)
+ self.deathImpulse = nil
+ self.deathPoint = nil
+
+ end
+
+ if deathTime then
+ self.timeToDestroy = deathTime
+ end
+
+ end
+
+end
+AddFunctionContract(RagdollMixin.SetRagdoll, { Arguments = { "Entity", { "number", "nil" } }, Returns = { } })
+
+function RagdollMixin:OnUpdate(deltaTime)
+
+ // Process outside of OnProcessMove() because animations can't be set there.
+ if Server then
+ self:_UpdateJustKilled()
+ self:_UpdateTimeToDestroy(deltaTime)
+ end
+
+end
+AddFunctionContract(RagdollMixin.OnUpdate, { Arguments = { "Entity", "number" }, Returns = { } })
+
+function RagdollMixin:_UpdateJustKilled()
+
+ if self.justKilled then
+
+ // Clear current animation so we know if it was set in TriggerEffects
+ self:SetAnimation("", true)
+
+ self:TriggerEffects("death")
+
+ // Destroy immediately if death animation or ragdoll wasn't triggered (used queued because we're in OnProcessMove)
+ local anim = self:GetAnimation()
+ if (self:GetPhysicsGroup() == PhysicsGroup.RagdollGroup) or (anim ~= nil and anim ~= "") then
+
+ if self.timeToDestroy == nil then
+ // Set default time to destroy so it's impossible to have things lying around.
+ self.timeToDestroy = 4
+ end
+
+ else
+ self:SafeDestroy()
+ end
+
+ self.justKilled = nil
+
+ end
+
+end
+AddFunctionContract(RagdollMixin._UpdateJustKilled, { Arguments = { "Entity" }, Returns = { } })
+
+function RagdollMixin:_UpdateTimeToDestroy(deltaTime)
+
+ if self.timeToDestroy then
+
+ self.timeToDestroy = self.timeToDestroy - deltaTime
+
+ if self.timeToDestroy <= 0 then
+
+ self:SafeDestroy()
+ self.timeToDestroy = nil
+
+ end
+
+ end
+
+end
+AddFunctionContract(RagdollMixin._UpdateTimeToDestroy, { Arguments = { "Entity" }, Returns = { } })
+
+function RagdollMixin:SafeDestroy()
+
+ // Note: This should be moved somewhere else soon.
+ if self.GetIsOnFire and self:GetIsOnFire() then
+ self:TriggerEffects("fire_stop")
+ end
+
+ if self:GetIsMapEntity() then
+
+ self:SetIsAlive(false)
+ self:SetIsVisible(false)
+ self:SetPhysicsType(PhysicsType.None)
+
+ else
+
+ DestroyEntity(self)
+
+ end
+
+end
+AddFunctionContract(RagdollMixin.SafeDestroy, { Arguments = { "Entity" }, Returns = { } })
\ No newline at end of file
diff --git a/ns2/lua/ResourcePoint.lua b/ns2/lua/ResourcePoint.lua
index d2a570b..2a477ba 100644
--- a/ns2/lua/ResourcePoint.lua
+++ b/ns2/lua/ResourcePoint.lua
@@ -33,7 +33,7 @@ function ResourcePoint:OnInit()
self:SetPhysicsGroup(PhysicsGroup.AttachClassGroup)
// Make the nozzle kinematic so that the player will collide with it.
- self:SetPhysicsType(Actor.PhysicsType.Kinematic)
+ self:SetPhysicsType(PhysicsType.Kinematic)
self:SetTechId(kTechId.ResourcePoint)
diff --git a/ns2/lua/ResourceTower.lua b/ns2/lua/ResourceTower.lua
index bc1d027..af0cd51 100644
--- a/ns2/lua/ResourceTower.lua
+++ b/ns2/lua/ResourceTower.lua
@@ -8,6 +8,7 @@
//
// ========= For more information, visit us at http://www.unknownworlds.com =====================
Script.Load("lua/Structure.lua")
+Script.Load("lua/RagdollMixin.lua")
class 'ResourceTower' (Structure)
@@ -23,7 +24,7 @@ ResourceTower.kMaxUpgradeLevel = 3
// they find. Same as in NS1.
ResourceTower.kBuildDelay = 4
-local networkVars =
+ResourceTower.networkVars =
{
upgradeLevel = string.format("integer (0 to %d)", ResourceTower.kMaxUpgradeLevel)
}
@@ -32,6 +33,14 @@ if (Server) then
Script.Load("lua/ResourceTower_Server.lua")
end
+function ResourceTower:OnCreate()
+
+ Structure.OnCreate(self)
+
+ InitMixin(self, RagdollMixin)
+
+end
+
function ResourceTower:OnInit()
Structure.OnInit(self)
@@ -56,18 +65,4 @@ function ResourceTower:GiveResourcesToTeam(player)
end
-/*
-function ResourceTower:GetDescription()
-
- local description = Structure.GetDescription(self)
-
- // Add upgrade level
- local upgradeLevel = self:GetUpgradeLevel()
- description = string.format("%s - +%d of %d", description, self:GetUpgradeLevel(), ResourceTower.kMaxUpgradeLevel)
-
- return description
-
-end
-*/
-
-Shared.LinkClassToMap("ResourceTower", ResourceTower.kMapName, networkVars)
+Shared.LinkClassToMap("ResourceTower", ResourceTower.kMapName, ResourceTower.networkVars)
diff --git a/ns2/lua/RoboticsFactory.lua b/ns2/lua/RoboticsFactory.lua
index b916903..e970c52 100644
--- a/ns2/lua/RoboticsFactory.lua
+++ b/ns2/lua/RoboticsFactory.lua
@@ -6,6 +6,7 @@
//
// ========= For more information, visit us at http://www.unknownworlds.com =====================
Script.Load("lua/Structure.lua")
+Script.Load("lua/RagdollMixin.lua")
class 'RoboticsFactory' (Structure)
@@ -26,13 +27,21 @@ local networkVars =
currentBuiltId = "entityid"
}
+function RoboticsFactory:OnCreate()
+
+ Structure.OnCreate(self)
+
+ InitMixin(self, RagdollMixin)
+
+end
+
function RoboticsFactory:OnInit()
self:SetModel(RoboticsFactory.kModelName)
Structure.OnInit(self)
- self:SetPhysicsType(Actor.PhysicsType.Kinematic)
+ self:SetPhysicsType(PhysicsType.Kinematic)
self.currentArcId = Entity.invalidId
diff --git a/ns2/lua/ScoringMixin.lua b/ns2/lua/ScoringMixin.lua
new file mode 100644
index 0000000..a26d8b4
--- /dev/null
+++ b/ns2/lua/ScoringMixin.lua
@@ -0,0 +1,49 @@
+// ======= Copyright © 2003-2011, Unknown Worlds Entertainment, Inc. All rights reserved. =======
+//
+// lua\ScoringMixin.lua
+//
+// Created by: Brian Cronin (brianc@unknownworlds.com)
+//
+// ========= For more information, visit us at http://www.unknownworlds.com =====================
+
+Script.Load("lua/FunctionContracts.lua")
+
+/**
+ * ScoringMixin keeps track of a score. It provides function to allow changing the score.
+ */
+ScoringMixin = { }
+ScoringMixin.type = "Scoring"
+
+ScoringMixin.expectedCallbacks =
+{
+ SetScoreboardChanged = "Called to notify the entity that the score has changed and should be updated on the client's scoreboard."
+}
+
+function ScoringMixin:__initmixin()
+
+ self.score = 0
+
+end
+
+function ScoringMixin:GetScore()
+ return self.score
+end
+AddFunctionContract(ScoringMixin.GetScore, { Arguments = { "Entity" }, Returns = { "number" } })
+
+function ScoringMixin:AddScore(points, res)
+
+ // Should only be called on the Server.
+ assert(Server and Client == nil)
+
+ // Tell client to display cool effect.
+ if points ~= nil and points ~= 0 then
+
+ local displayRes = ConditionalValue(type(res) == "number", res, 0)
+ Server.SendCommand(self, string.format("points %s %s", tostring(points), tostring(displayRes)))
+ self.score = Clamp(self.score + points, 0, self:GetMixinConstants().kMaxScore or 100)
+ self:SetScoreboardChanged(true)
+
+ end
+
+end
+AddFunctionContract(ScoringMixin.AddScore, { Arguments = { "Entity", "number", { "number", "nil" } }, Returns = { } })
\ No newline at end of file
diff --git a/ns2/lua/SelectableMixin.lua b/ns2/lua/SelectableMixin.lua
new file mode 100644
index 0000000..1dbf475
--- /dev/null
+++ b/ns2/lua/SelectableMixin.lua
@@ -0,0 +1,39 @@
+// ======= Copyright © 2003-2011, Unknown Worlds Entertainment, Inc. All rights reserved. =======
+//
+// lua\SelectableMixin.lua
+//
+// Created by: Brian Cronin (brianc@unknownworlds.com)
+//
+// ========= For more information, visit us at http://www.unknownworlds.com =====================
+
+Script.Load("lua/FunctionContracts.lua")
+
+/**
+ * SelectableMixin marks entities as selectable to a commander.
+ */
+SelectableMixin = { }
+SelectableMixin.type = "Selectable"
+
+SelectableMixin.optionalCallbacks =
+{
+ OnGetIsSelectable = "Passes in a table with a Selectable field that can be set to true or false."
+}
+
+function SelectableMixin:__initmixin()
+end
+
+function SelectableMixin:GetIsSelectable()
+
+ if self.OnGetIsSelectable then
+
+ // Assume selectable by default.
+ local selectableTable = { Selectable = true }
+ self:OnGetIsSelectable(selectableTable)
+ return selectableTable.Selectable
+
+ end
+
+ return true
+
+end
+AddFunctionContract(SelectableMixin.GetIsSelectable, { Arguments = { "Entity" }, Returns = { "boolean" } })
\ No newline at end of file
diff --git a/ns2/lua/Sentry.lua b/ns2/lua/Sentry.lua
index c6806cf..ccfb54c 100644
--- a/ns2/lua/Sentry.lua
+++ b/ns2/lua/Sentry.lua
@@ -6,6 +6,7 @@
//
// ========= For more information, visit us at http://www.unknownworlds.com =====================
Script.Load("lua/Structure.lua")
+Script.Load("lua/RagdollMixin.lua")
class 'Sentry' (Structure)
@@ -96,6 +97,8 @@ function Sentry:OnCreate()
Structure.OnCreate(self)
+ InitMixin(self, RagdollMixin)
+
self.desiredYawDegrees = 0
self.desiredPitchDegrees = 0
self.barrelYawDegrees = 0
@@ -340,7 +343,7 @@ function Sentry:UpdatePoseParameters(deltaTime)
end
-function Sentry:GetCanDoDamage()
+function Sentry:GetCanGiveDamageOverride()
return true
end
diff --git a/ns2/lua/Shade.lua b/ns2/lua/Shade.lua
index ae3e63b..4b1995a 100644
--- a/ns2/lua/Shade.lua
+++ b/ns2/lua/Shade.lua
@@ -15,12 +15,13 @@
// team to get a stealth hive built. Allow players to stay cloaked for awhile, until they attack
// (even if they move out of range - great for getting by sentries).
//
-// Phantasm (Targeted) - Allow Commander to create fake Fade, Onos, Hive (and possibly
+// Phantom (Targeted) - Allow Commander to create fake Fade, Onos, Hive (and possibly
// ammo/medpacks). They can be pathed around and used to create tactical distractions or divert
// forces elsewhere.
//
// ========= For more information, visit us at http://www.unknownworlds.com =====================
Script.Load("lua/Structure.lua")
+Script.Load("lua/RagdollMixin.lua")
class 'Shade' (Structure)
@@ -34,6 +35,13 @@ Shade.kCloakRadius = 15
// when cloak is triggered, we cloak everything around us at this interval
Shade.kActiveThinkInterval = 3
+function Shade:OnCreate()
+
+ Structure.OnCreate(self)
+
+ InitMixin(self, RagdollMixin)
+
+end
function Shade:GetIsAlienStructure()
return true
@@ -45,7 +53,7 @@ function Shade:GetTechButtons(techId)
if techId == kTechId.RootMenu then
- techButtons = { kTechId.UpgradesMenu, kTechId.ShadeDisorient, kTechId.ShadeCloak, kTechId.ShadePhantasmMenu }
+ techButtons = { kTechId.UpgradesMenu, kTechId.ShadeDisorient, kTechId.ShadeCloak, kTechId.ShadePhantomMenu }
// Allow structure to be upgraded to mature version
local upgradeIndex = table.maxn(techButtons) + 1
@@ -59,9 +67,9 @@ function Shade:GetTechButtons(techId)
techButtons = {kTechId.CamouflageTech, kTechId.FeintTech, kTechId.None}
techButtons[kAlienBackButtonIndex] = kTechId.RootMenu
- elseif techId == kTechId.ShadePhantasmMenu then
+ elseif techId == kTechId.ShadePhantomMenu then
- techButtons = {kTechId.ShadePhantasmFade, kTechId.ShadePhantasmOnos, kTechId.ShadePhantasmHive}
+ techButtons = {kTechId.ShadePhantomFade, kTechId.ShadePhantomOnos}
techButtons[kAlienBackButtonIndex] = kTechId.RootMenu
end
@@ -79,7 +87,7 @@ function Shade:OnResearchComplete(structure, researchId)
// Transform into mature shade
if structure and (structure:GetId() == self:GetId()) and (researchId == kTechId.UpgradeShade) then
- success = self:Upgrade(kTechId.MatureShade)
+ success = self:UpgradeToTechId(kTechId.MatureShade)
end
@@ -147,6 +155,23 @@ function Shade:GetActivationTechAllowed(techId)
return true
end
+function CreatePhantom(techId, position, normal, commander)
+
+ // Create phantom effigy nearby
+ local phantomEffigy = CreateEntityForCommander(kTechId.PhantomEffigy, position, commander)
+ phantomEffigy:SetTechId(techId)
+
+ // Set model
+ local modelName = LookupTechData(techId, kTechDataModel)
+ phantomEffigy:SetModel(modelName)
+
+ // Don't let enemy team see this, but allow ready room and friendly commander/team-mates
+ phantomEffigy:SetExcludeRelevancyMask( ConditionalValue(commander:GetTeamNumber() == 1, kRelevantToTeam2Unit, kRelevantToTeam1Unit) )
+
+ return true
+
+end
+
function Shade:PerformActivation(techId, position, normal, commander)
local success = false
@@ -155,6 +180,10 @@ function Shade:PerformActivation(techId, position, normal, commander)
success = self:TriggerCloak()
+ elseif (techId == kTechId.ShadePhantomFade) or (techId == kTechId.ShadePhantomOnos) then
+
+ success = CreatePhantom(techId, position, normal, commander)
+
end
return success
diff --git a/ns2/lua/Shared.lua b/ns2/lua/Shared.lua
index c043004..0f98a47 100644
--- a/ns2/lua/Shared.lua
+++ b/ns2/lua/Shared.lua
@@ -93,8 +93,7 @@ Script.Load("lua/Drifter.lua")
Script.Load("lua/Egg.lua")
Script.Load("lua/Embryo.lua")
Script.Load("lua/Cocoon.lua")
-Script.Load("lua/Phantasm.lua")
-Script.Load("lua/OnosPhantasm.lua")
+Script.Load("lua/PhantomEffigy.lua")
// Base players
Script.Load("lua/ReadyRoomPlayer.lua")
diff --git a/ns2/lua/Shift.lua b/ns2/lua/Shift.lua
index 26b0be9..d6dba6f 100644
--- a/ns2/lua/Shift.lua
+++ b/ns2/lua/Shift.lua
@@ -14,6 +14,7 @@
//
// ========= For more information, visit us at http://www.unknownworlds.com =====================
Script.Load("lua/Structure.lua")
+Script.Load("lua/RagdollMixin.lua")
class 'Shift' (Structure)
@@ -31,6 +32,14 @@ Shift.kEnergizeEffect = PrecacheAsset("cinematics/alien/shift/energize.cinematic
Shift.kEnergizeSmallTargetEffect = PrecacheAsset("cinematics/alien/shift/energize_small.cinematic")
Shift.kEnergizeLargeTargetEffect = PrecacheAsset("cinematics/alien/shift/energize_large.cinematic")
+function Shift:OnCreate()
+
+ Structure.OnCreate(self)
+
+ InitMixin(self, RagdollMixin)
+
+end
+
function Shift:GetIsAlienStructure()
return true
end
@@ -70,7 +79,7 @@ function Shift:OnResearchComplete(structure, researchId)
// Transform into mature shift
if structure and (structure:GetId() == self:GetId()) and (researchId == kTechId.UpgradeShift) then
- success = self:Upgrade(kTechId.MatureShift)
+ success = self:UpgradeToTechId(kTechId.MatureShift)
end
diff --git a/ns2/lua/Structure.lua b/ns2/lua/Structure.lua
index 3c4f787..47e3f3c 100644
--- a/ns2/lua/Structure.lua
+++ b/ns2/lua/Structure.lua
@@ -9,7 +9,10 @@
// ========= For more information, visit us at http://www.unknownworlds.com =====================
Script.Load("lua/Balance.lua")
Script.Load("lua/LiveScriptActor.lua")
+Script.Load("lua/UpgradableMixin.lua")
+Script.Load("lua/PointGiverMixin.lua")
Script.Load("lua/GameEffectsMixin.lua")
+Script.Load("lua/SelectableMixin.lua")
Script.Load("lua/FlinchMixin.lua")
Script.Load("lua/CloakableMixin.lua")
Script.Load("lua/TargetMixin.lua")
@@ -68,6 +71,7 @@ Structure.networkVars =
}
PrepareClassForMixin(Structure, EnergyMixin)
+PrepareClassForMixin(Structure, UpgradableMixin)
PrepareClassForMixin(Structure, GameEffectsMixin)
PrepareClassForMixin(Structure, FlinchMixin)
PrepareClassForMixin(Structure, CloakableMixin)
@@ -76,8 +80,11 @@ function Structure:OnCreate()
LiveScriptActor.OnCreate(self)
+ InitMixin(self, UpgradableMixin)
InitMixin(self, GameEffectsMixin)
InitMixin(self, FlinchMixin)
+ InitMixin(self, PointGiverMixin)
+ InitMixin(self, SelectableMixin)
InitMixin(self, CloakableMixin)
InitMixin(self, PathingMixin)
@@ -91,7 +98,7 @@ function Structure:OnCreate()
self:SetUpdates(true)
// Make the structure kinematic so that the player will collide with it.
- self:SetPhysicsType(Actor.PhysicsType.Kinematic)
+ self:SetPhysicsType(PhysicsType.Kinematic)
self.effectsActive = false
@@ -244,8 +251,9 @@ function Structure:GetTechAllowed(techId, techNode, player)
end
function Structure:GetStatusDescription()
-
- if (not self:GetIsBuilt() ) then
+ if (self:GetRecycleActive()) then
+ return Locale.ResolveString("RECYCLING") .. "...", self:GetResearchProgress()
+ elseif (not self:GetIsBuilt() ) then
return Locale.ResolveString("CONSTRUCTING") .. "...", self:GetBuiltFraction()
@@ -354,4 +362,8 @@ function Structure:OverrideVisionRadius()
return LOSMixin.kStructureMinLOSDistance
end
+function Structure:GetRecycleActive()
+ return self.researchingId == kTechId.Recycle
+end
+
Shared.LinkClassToMap("Structure", Structure.kMapName, Structure.networkVars)
diff --git a/ns2/lua/Structure_Server.lua b/ns2/lua/Structure_Server.lua
index 91356ff..da1a06b 100644
--- a/ns2/lua/Structure_Server.lua
+++ b/ns2/lua/Structure_Server.lua
@@ -68,8 +68,9 @@ function Structure:OnUse(player, elapsedTime, useAttachPoint, usePoint)
end
function Structure:UpdateResearch(timePassed)
-
- if (self:GetIsBuilt() and (self.researchingId ~= kTechId.None)) then
+ local shouldUpdate = (self:GetIsBuilt() or self:GetRecycleActive())
+
+ if (shouldUpdate and (self.researchingId ~= kTechId.None)) then
local timePassed = Shared.GetTime() - self.timeResearchStarted
@@ -100,33 +101,27 @@ function Structure:UpdateRecycle(timePassed)
// TODO:
end
-function Structure:Upgrade(newTechId)
+function Structure:OnPreUpgradeToTechId(newTechId)
- if self:GetTechId() ~= newTechId then
-
- // Preserve health and armor scalars but potentially change maxHealth and maxArmor
- local energyScalar = self.energy / self.maxEnergy
-
- self.maxEnergy = LookupTechData(newTechId, kTechDataMaxEnergy, self.maxEnergy)
-
- self.energy = energyScalar * self.maxEnergy
-
- return LiveScriptActor.Upgrade(self, newTechId)
-
- end
+ // Preserve health and armor scalars but potentially change maxHealth and maxArmor.
+ local energyScalar = self.energy / self.maxEnergy
- return false
+ self.maxEnergy = LookupTechData(newTechId, kTechDataMaxEnergy, self.maxEnergy)
+
+ self.energy = energyScalar * self.maxEnergy
end
function Structure:UpdateStructure(timePassed)
- if self:GetIsBuilt() then
+ local shouldUpdate = (self:GetIsBuilt() or self:GetRecycleActive())
- self:UpdateResearch(timePassed)
-
+ if shouldUpdate then
+ self:UpdateResearch(timePassed)
+ end
+
+ if self:GetIsBuilt() then
self:UpdateEnergy(timePassed)
-
end
self:UpdateRecycle(timePassed)
@@ -182,8 +177,6 @@ end
// Play hurt or wound effects
function Structure:OnTakeDamage(damage, attacker, doer, point)
- LiveScriptActor.OnTakeDamage(self, damage, attacker, doer, point)
-
local team = self:GetTeam()
if team.TriggerAlert then
team:TriggerAlert(self:GetDamagedAlertId(), self)
diff --git a/ns2/lua/TargetCache.lua b/ns2/lua/TargetCache.lua
index 9966d84..4c09208 100644
--- a/ns2/lua/TargetCache.lua
+++ b/ns2/lua/TargetCache.lua
@@ -90,7 +90,7 @@ end
// Selects targets based on if they can hurt us
//
function HarmfulPrioritizer()
- return function(target) return target:GetCanDoDamage() end
+ return function(target) return target:GetCanGiveDamage() end
end
//
diff --git a/ns2/lua/TechData.lua b/ns2/lua/TechData.lua
index df7ae16..c5985b1 100644
--- a/ns2/lua/TechData.lua
+++ b/ns2/lua/TechData.lua
@@ -325,6 +325,7 @@ function BuildTechData()
{ [kTechDataId] = kTechId.Cyst, [kTechDataMapName] = Cyst.kMapName, [kTechDataDisplayName] = "CYST", [kTechDataTooltipInfo] = "CYST_TOOLTIP", [kTechDataCostKey] = kCystCost, [kTechDataBuildTime] = kCystBuildTime, [kTechDataMaxHealth] = kCystHealth, [kTechDataMaxArmor] = kCystArmor, [kTechDataModel] = Cyst.kModelName, [kVisualRange] = Cyst.kInfestRadius, [kTechDataRequiresInfestation] = false, [kTechDataPointValue] = kCystPointValue, [kTechDataGrows] = false, [kTechDataBuildRequiresMethod]=GetCystParentAvailable, [kTechDataGhostGuidesMethod]=GetCystGhostGuides, /* [kStructureBuildNearClass] = {"Hive", "Cyst"}, [kStructureAttachId] = {kTechId.Hive, kTechId.Cyst}, [kStructureAttachRange] = kHiveCystParentRange */} ,
{ [kTechDataId] = kTechId.MiniCyst, [kTechDataMapName] = MiniCyst.kMapName, [kTechDataDisplayName] = "MINI_CYST", [kTechDataCostKey] = kMiniCystCost, [kTechDataBuildTime] = kMiniCystBuildTime, [kTechDataMaxHealth] = kMiniCystHealth, [kTechDataMaxArmor] = kMiniCystArmor, [kTechDataModel] = MiniCyst.kModelName, [kVisualRange] = MiniCyst.kInfestRadius, [kTechDataRequiresInfestation] = false, [kTechDataPointValue] = kMiniCystPointValue, [kTechDataGrows] = false},
+
// Alien structure abilities and their energy costs
{ [kTechDataId] = kTechId.CragHeal, [kTechDataDisplayName] = "HEAL", [kTechDataHotkey] = Move.H, [kTechDataCostKey] = kCragHealCost, [kTechDataTooltipInfo] = "CRAG_HEAL_TOOLTIP"},
{ [kTechDataId] = kTechId.CragUmbra, [kTechDataDisplayName] = "UMBRA", [kTechDataHotkey] = Move.M, [kTechDataCostKey] = kCragUmbraCost, [kVisualRange] = Crag.kHealRadius, [kTechDataTooltipInfo] = "CRAG_UMBRA_TOOLTIP"},
@@ -339,10 +340,10 @@ function BuildTechData()
{ [kTechDataId] = kTechId.ShadeDisorient, [kTechDataDisplayName] = "DISORIENT", [kTechDataHotkey] = Move.D, [kTechDataTooltipInfo] = "SHADE_DISORIENT_TOOLTIP"},
{ [kTechDataId] = kTechId.ShadeCloak, [kTechDataDisplayName] = "CLOAK", [kTechDataHotkey] = Move.C, [kTechDataCostKey] = kShadeCloakCost },
- { [kTechDataId] = kTechId.ShadePhantasmMenu, [kTechDataDisplayName] = "PHANTASM", [kTechDataHotkey] = Move.P },
- //{ [kTechDataId] = kTechId.ShadePhantasmFade, [kTechDataDisplayName] = "PHANTASM FADE", [kTechDataModel] = Fade.kModelName, [kTechDataMapName] = FadePhantasm.kMapName, [kTechDataHotkey] = Move.F, [kTechDataCostKey] = kShadePhantasmFadeEnergyCost },
- // { [kTechDataId] = kTechId.ShadePhantasmOnos, [kTechDataDisplayName] = "PHANTASM ONOS", [kTechDataModel] = Onos.kModelName, [kTechDataMapName] = OnosPhantasm.kMapName, [kTechDataHotkey] = Move.O, [kTechDataCostKey] = kShadePhantasmOnosEnergyCost },
- //{ [kTechDataId] = kTechId.ShadePhantasmHive, [kTechDataDisplayName] = "PHANTASM HIVE", [kTechDataModel] = Hive.kModelName, [kTechDataMapName] = HivePhantasm.kMapName, [kTechDataHotkey] = Move.H, [kTechDataCostKey] = kShadePhantasmHiveEnergyCost, [kStructureAttachClass] = "TechPoint", },
+ { [kTechDataId] = kTechId.ShadePhantomMenu, [kTechDataDisplayName] = "PHANTOM", [kTechDataHotkey] = Move.P },
+ { [kTechDataId] = kTechId.ShadePhantomFade, [kTechDataDisplayName] = "PHANTOM_FADE", [kTechDataModel] = Fade.kModelName, [kTechDataMapName] = PhantomEffigy.kMapName, [kTechDataHotkey] = Move.F, [kTechDataCostKey] = kShadePhantomFadeEnergyCost },
+ { [kTechDataId] = kTechId.ShadePhantomOnos, [kTechDataDisplayName] = "PHANTOM_ONOS", [kTechDataModel] = Onos.kModelName, [kTechDataMapName] = PhantomEffigy.kMapName, [kTechDataHotkey] = Move.O, [kTechDataCostKey] = kShadePhantomOnosEnergyCost },
+ //{ [kTechDataId] = kTechId.ShadePhantasmHive, [kTechDataDisplayName] = "PHANTOM_HIVE", [kTechDataModel] = Hive.kModelName, [kTechDataMapName] = HivePhantasm.kMapName, [kTechDataHotkey] = Move.H, [kTechDataCostKey] = kShadePhantasmHiveEnergyCost, [kStructureAttachClass] = "TechPoint", },
{ [kTechDataId] = kTechId.WhipUnroot, [kTechDataDisplayName] = "UNROOT_WHIP", [kTechDataTooltipInfo] = "UNROOT_WHIP_TOOLTIP"},
{ [kTechDataId] = kTechId.WhipRoot, [kTechDataDisplayName] = "ROOT_WHIP", [kTechDataTooltipInfo] = "ROOT_WHIP_TOOLTIP"},
diff --git a/ns2/lua/TechPoint.lua b/ns2/lua/TechPoint.lua
index 210c941..2c4fe77 100644
--- a/ns2/lua/TechPoint.lua
+++ b/ns2/lua/TechPoint.lua
@@ -48,7 +48,7 @@ function TechPoint:OnInit()
self:SetPhysicsGroup(PhysicsGroup.AttachClassGroup)
// Make the nozzle kinematic so that the player will collide with it.
- self:SetPhysicsType(Actor.PhysicsType.Kinematic)
+ self:SetPhysicsType(PhysicsType.Kinematic)
self:SetTechId(kTechId.TechPoint)
diff --git a/ns2/lua/TechPoint_Server.lua b/ns2/lua/TechPoint_Server.lua
index 52ba714..0bbb10d 100644
--- a/ns2/lua/TechPoint_Server.lua
+++ b/ns2/lua/TechPoint_Server.lua
@@ -6,7 +6,7 @@
//
// ========= For more information, visit us at http://www.unknownworlds.com =====================
-function TechPoint:GetCanTakeDamage()
+function TechPoint:GetCanTakeDamageOverride()
return false
end
diff --git a/ns2/lua/TechTreeButtons.lua b/ns2/lua/TechTreeButtons.lua
index 1c8d048..de10a73 100644
--- a/ns2/lua/TechTreeButtons.lua
+++ b/ns2/lua/TechTreeButtons.lua
@@ -290,11 +290,10 @@ function InitTechTreeMaterialOffsets()
kAlienTechIdToMaterialOffset[kTechId.MatureShade] = 66
kAlienTechIdToMaterialOffset[kTechId.ShadeCloak] = 67
kAlienTechIdToMaterialOffset[kTechId.ShadeDisorient] = 68
- kAlienTechIdToMaterialOffset[kTechId.ShadePhantasmMenu] = 69
- kAlienTechIdToMaterialOffset[kTechId.ShadePhantasmFade] = 69
- kAlienTechIdToMaterialOffset[kTechId.ShadePhantasmOnos] = 69
- kAlienTechIdToMaterialOffset[kTechId.ShadePhantasmHive] = 69
- kAlienTechIdToMaterialOffset[kTechId.PhantasmTech] = 70
+ kAlienTechIdToMaterialOffset[kTechId.ShadePhantomMenu] = 69
+ kAlienTechIdToMaterialOffset[kTechId.ShadePhantomFade] = 69
+ kAlienTechIdToMaterialOffset[kTechId.ShadePhantomOnos] = 69
+ kAlienTechIdToMaterialOffset[kTechId.PhantomTech] = 70
kAlienTechIdToMaterialOffset[kTechId.CamouflageTech] = 71
kAlienTechIdToMaterialOffset[kTechId.Camouflage] = 71
diff --git a/ns2/lua/TechTreeConstants.lua b/ns2/lua/TechTreeConstants.lua
index a6f76be..9db1024 100644
--- a/ns2/lua/TechTreeConstants.lua
+++ b/ns2/lua/TechTreeConstants.lua
@@ -108,13 +108,13 @@ kTechId = enum({
'Crag', 'UpgradeCrag', 'MatureCrag', 'CragHeal', 'CragUmbra', 'CragBabblers',
'Whip', 'UpgradeWhip', 'MatureWhip', 'WhipAcidStrike', 'WhipFury', 'WhipBombard',
'Shift', 'UpgradeShift', 'MatureShift', 'ShiftRecall', 'ShiftEcho', 'ShiftEnergize',
- 'Shade', 'UpgradeShade', 'MatureShade', 'ShadeDisorient', 'ShadeCloak', 'ShadePhantasmMenu', 'ShadePhantasmFade', 'ShadePhantasmOnos', 'ShadePhantasmHive',
+ 'Shade', 'UpgradeShade', 'MatureShade', 'ShadeDisorient', 'ShadeCloak', 'ShadePhantomMenu', 'ShadePhantomFade', 'ShadePhantomOnos',
// Whip movement
'WhipRoot', 'WhipUnroot',
// Alien abilities and upgrades - BabblerTech
- 'BabblerTech', 'EchoTech', 'PhantasmTech',
+ 'BabblerTech', 'EchoTech', 'PhantomTech', 'PhantomEffigy',
'Melee1Tech', 'Melee2Tech', 'Melee3Tech', 'AlienArmor1Tech', 'AlienArmor2Tech', 'AlienArmor3Tech',
'AdrenalineTech', 'BileBombTech', 'LeapTech', 'BacteriaTech', 'FeintTech', 'SapTech', 'StompTech', 'BoneShieldTech', 'CarapaceTech', 'PiercingTech',
'FrenzyTech', 'SwarmTech', 'RegenerationTech', 'CamouflageTech',
diff --git a/ns2/lua/TimedCallbackMixin.lua b/ns2/lua/TimedCallbackMixin.lua
index 3f35e29..1670307 100644
--- a/ns2/lua/TimedCallbackMixin.lua
+++ b/ns2/lua/TimedCallbackMixin.lua
@@ -24,7 +24,7 @@ function TimedCallbackMixin:AddTimedCallback(addFunction, callRate)
end
AddFunctionContract(TimedCallbackMixin.AddTimedCallback, { Arguments = { "Entity", "function", "number" }, Returns = { } })
-function TimedCallbackMixin:UpdateTimedCallbacks(deltaTime)
+function TimedCallbackMixin:OnUpdate(deltaTime)
if self.timedCallbacks then
@@ -58,4 +58,4 @@ function TimedCallbackMixin:UpdateTimedCallbacks(deltaTime)
end
end
-AddFunctionContract(TimedCallbackMixin.UpdateTimedCallbacks, { Arguments = { "Entity", "number" }, Returns = { } })
\ No newline at end of file
+AddFunctionContract(TimedCallbackMixin.OnUpdate, { Arguments = { "Entity", "number" }, Returns = { } })
\ No newline at end of file
diff --git a/ns2/lua/UpgradableMixin.lua b/ns2/lua/UpgradableMixin.lua
new file mode 100644
index 0000000..c13d161
--- /dev/null
+++ b/ns2/lua/UpgradableMixin.lua
@@ -0,0 +1,183 @@
+// ======= Copyright © 2003-2011, Unknown Worlds Entertainment, Inc. All rights reserved. =======
+//
+// lua\UpgradableMixin.lua
+//
+// Created by: Brian Cronin (brianc@unknownworlds.com)
+//
+// ========= For more information, visit us at http://www.unknownworlds.com =====================
+
+Script.Load("lua/FunctionContracts.lua")
+
+/**
+ * UpgradableMixin handles two forms of upgrades. There are the upgrades that it owns (upgrade1 - upgrade4).
+ * It can also handle upgrading the entire entity to another tech Id independent of the upgrades it owns.
+ */
+UpgradableMixin = { }
+UpgradableMixin.type = "Upgradable"
+
+UpgradableMixin.expectedCallbacks =
+{
+ SetTechId = "Sets the current tech Id of this entity.",
+ GetTechId = "Returns the current tech Id of this entity."
+}
+
+UpgradableMixin.optionalCallbacks =
+{
+ OnPreUpgradeToTechId = "Called right before upgrading to a new tech Id.",
+ OnGiveUpgrade = "Called to notify that an upgrade was given with the tech Id as the single parameter."
+}
+
+function UpgradableMixin.__prepareclass(toClass)
+
+ ASSERT(toClass.networkVars ~= nil, "UpgradableMixin expects the class to have network fields")
+
+ local addNetworkFields =
+ {
+ upgrade1 = "enum kTechId",
+ upgrade2 = "enum kTechId",
+ upgrade3 = "enum kTechId",
+ upgrade4 = "enum kTechId",
+ }
+
+ for k, v in pairs(addNetworkFields) do
+ toClass.networkVars[k] = v
+ end
+
+end
+
+function UpgradableMixin:__initmixin()
+
+ self.upgrade1 = kTechId.None
+ self.upgrade2 = kTechId.None
+ self.upgrade3 = kTechId.None
+ self.upgrade4 = kTechId.None
+
+end
+
+function UpgradableMixin:UpgradeToTechId(newTechId)
+
+ if self:GetTechId() ~= newTechId then
+
+ if self.OnPreUpgradeToTechId then
+ self:OnPreUpgradeToTechId(newTechId)
+ end
+
+ local healthScalar = 0
+ local armorScalar = 0
+ local isAlive = HasMixin(self, "Live")
+ if isAlive then
+ // Preserve health and armor scalars but potentially change maxHealth and maxArmor.
+ healthScalar = self:GetHealthScalar()
+ armorScalar = self:GetArmorScalar()
+ end
+
+ self:SetTechId(newTechId)
+
+ if isAlive then
+
+ self:SetMaxHealth(LookupTechData(newTechId, kTechDataMaxHealth, self:GetMaxHealth()))
+ self:SetMaxArmor(LookupTechData(newTechId, kTechDataMaxArmor, self:GetMaxArmor()))
+
+ self:SetHealth(healthScalar * self:GetMaxHealth())
+ self:SetArmor(armorScalar * self:GetMaxArmor())
+
+ end
+
+ return true
+
+ end
+
+ return false
+
+end
+AddFunctionContract(UpgradableMixin.UpgradeToTechId, { Arguments = { "Entity", "number" }, Returns = { "boolean" } })
+
+function UpgradableMixin:GetHasUpgrade(techId)
+ return techId ~= kTechId.None and (techId == self.upgrade1 or techId == self.upgrade2 or techId == self.upgrade3 or techId == self.upgrade4)
+end
+AddFunctionContract(UpgradableMixin.GetHasUpgrade, { Arguments = { "Entity", "number" }, Returns = { "boolean" } })
+
+function UpgradableMixin:GetUpgrades()
+
+ local upgrades = { }
+
+ if self.upgrade1 ~= kTechId.None then
+ table.insert(upgrades, self.upgrade1)
+ end
+ if self.upgrade2 ~= kTechId.None then
+ table.insert(upgrades, self.upgrade2)
+ end
+ if self.upgrade3 ~= kTechId.None then
+ table.insert(upgrades, self.upgrade3)
+ end
+ if self.upgrade4 ~= kTechId.None then
+ table.insert(upgrades, self.upgrade4)
+ end
+
+ return upgrades
+
+end
+AddFunctionContract(UpgradableMixin.GetUpgrades, { Arguments = { "Entity" }, Returns = { "table" } })
+
+function UpgradableMixin:GiveUpgrade(techId)
+
+ local upgradeGiven = false
+
+ if not self:GetHasUpgrade(techId) then
+
+ if self.upgrade1 == kTechId.None then
+
+ self.upgrade1 = techId
+ upgradeGiven = true
+
+ elseif self.upgrade2 == kTechId.None then
+
+ self.upgrade2 = techId
+ upgradeGiven = true
+
+ elseif self.upgrade3 == kTechId.None then
+
+ self.upgrade3 = techId
+ upgradeGiven = true
+
+ elseif self.upgrade4 == kTechId.None then
+
+ self.upgrade4 = techId
+ upgradeGiven = true
+
+ end
+
+ assert(upgradeGiven, "Entity already has the max of four upgrades.")
+
+ else
+ error("Entity already has upgrade.")
+ end
+
+ if upgradeGiven and self.OnGiveUpgrade then
+ self:OnGiveUpgrade(techId)
+ end
+
+ return upgradeGiven
+
+end
+AddFunctionContract(UpgradableMixin.GiveUpgrade, { Arguments = { "Entity", "number" }, Returns = { "boolean" } })
+
+function UpgradableMixin:Reset()
+ self:_ClearUpgrades()
+end
+AddFunctionContract(UpgradableMixin.Reset, { Arguments = { "Entity" }, Returns = { } })
+
+function UpgradableMixin:OnKill()
+ self:_ClearUpgrades()
+end
+AddFunctionContract(UpgradableMixin.OnKill, { Arguments = { "Entity" }, Returns = { } })
+
+function UpgradableMixin:_ClearUpgrades()
+
+ self.upgrade1 = kTechId.None
+ self.upgrade2 = kTechId.None
+ self.upgrade3 = kTechId.None
+ self.upgrade4 = kTechId.None
+
+end
+AddFunctionContract(UpgradableMixin._ClearUpgrades, { Arguments = { "Entity" }, Returns = { } })
\ No newline at end of file
diff --git a/ns2/lua/Weapons/Alien/BileBomb.lua b/ns2/lua/Weapons/Alien/BileBomb.lua
index 50fa469..1ee4093 100644
--- a/ns2/lua/Weapons/Alien/BileBomb.lua
+++ b/ns2/lua/Weapons/Alien/BileBomb.lua
@@ -53,7 +53,7 @@ function BileBomb:FireBombProjectile(player)
local bomb = CreateEntity(Bomb.kMapName, startPoint, player:GetTeamNumber())
SetAnglesFromVector(bomb, viewCoords.zAxis)
- bomb:SetPhysicsType(Actor.PhysicsType.Kinematic)
+ bomb:SetPhysicsType(PhysicsType.Kinematic)
local startVelocity = viewCoords.zAxis * BileBomb.kBombSpeed
bomb:SetVelocity(startVelocity)
diff --git a/ns2/lua/Weapons/Alien/SpitSpray.lua b/ns2/lua/Weapons/Alien/SpitSpray.lua
index 1bb0d70..f955682 100644
--- a/ns2/lua/Weapons/Alien/SpitSpray.lua
+++ b/ns2/lua/Weapons/Alien/SpitSpray.lua
@@ -80,7 +80,7 @@ function SpitSpray:CreateSpitProjectile(player)
local spit = CreateEntity(Spit.kMapName, startPoint, player:GetTeamNumber())
SetAnglesFromVector(spit, viewCoords.zAxis)
- spit:SetPhysicsType(Actor.PhysicsType.Kinematic)
+ spit:SetPhysicsType(PhysicsType.Kinematic)
local startVelocity = viewCoords.zAxis * SpitSpray.kSpitSpeed
spit:SetVelocity(startVelocity)
diff --git a/ns2/lua/Weapons/Projectile_Server.lua b/ns2/lua/Weapons/Projectile_Server.lua
index 00a7c1b..da8592b 100644
--- a/ns2/lua/Weapons/Projectile_Server.lua
+++ b/ns2/lua/Weapons/Projectile_Server.lua
@@ -46,9 +46,9 @@ function Projectile:SetPhysicsType(physicsType)
if (self.physicsBody) then
- if (self.physicsType == Actor.PhysicsType.Kinematic) then
+ if (self.physicsType == PhysicsType.Kinematic) then
self.physicsBody:SetSimulationEnabled(false)
- elseif (self.physicsType == Actor.PhysicsType.Dynamic) then
+ elseif (self.physicsType == PhysicsType.Dynamic) then
self.physicsBody:SetSimulationEnabled(true)
end
diff --git a/ns2/lua/Weapons/Weapon_Client.lua b/ns2/lua/Weapons/Weapon_Client.lua
index fad4775..1b5016c 100644
--- a/ns2/lua/Weapons/Weapon_Client.lua
+++ b/ns2/lua/Weapons/Weapon_Client.lua
@@ -11,12 +11,12 @@ end
function Weapon:UpdateDropped()
- if self:GetPhysicsType() == Actor.PhysicsType.DynamicServer and not self.dropped then
+ if self:GetPhysicsType() == PhysicsType.DynamicServer and not self.dropped then
self:Dropped(nil)
self.dropped = true
- elseif self:GetPhysicsType() == Actor.PhysicsType.None then
+ elseif self:GetPhysicsType() == PhysicsType.None then
self.dropped = false
end
diff --git a/ns2/lua/Weapons/Weapon_Server.lua b/ns2/lua/Weapons/Weapon_Server.lua
index af0fd32..ce878d5 100644
--- a/ns2/lua/Weapons/Weapon_Server.lua
+++ b/ns2/lua/Weapons/Weapon_Server.lua
@@ -38,7 +38,7 @@ function Weapon:SetWeaponWorldState(state)
if state then
- self:SetPhysicsType(Actor.PhysicsType.DynamicServer)
+ self:SetPhysicsType(PhysicsType.DynamicServer)
// So it doesn't affect player movement and so collide callback is called
self:SetPhysicsGroup(PhysicsGroup.DroppedWeaponGroup)
@@ -56,7 +56,7 @@ function Weapon:SetWeaponWorldState(state)
else
- self:SetPhysicsType(Actor.PhysicsType.None)
+ self:SetPhysicsType(PhysicsType.None)
self:SetPhysicsGroup(PhysicsGroup.WeaponGroup)
self:UpdatePhysicsModel()
diff --git a/ns2/lua/Whip.lua b/ns2/lua/Whip.lua
index 9f13ed0..4a7520e 100644
--- a/ns2/lua/Whip.lua
+++ b/ns2/lua/Whip.lua
@@ -12,6 +12,7 @@
Script.Load("lua/Structure.lua")
Script.Load("lua/DoorMixin.lua")
Script.Load("lua/InfestationMixin.lua")
+Script.Load("lua/RagdollMixin.lua")
class 'Whip' (Structure)
@@ -54,6 +55,8 @@ function Whip:OnCreate()
Structure.OnCreate(self)
+ InitMixin(self, RagdollMixin)
+
self.attackYaw = 0
self.mode = Whip.kMode.Rooted
@@ -154,7 +157,7 @@ function Whip:UpdatePoseParameters(deltaTime)
end
-function Whip:GetCanDoDamage()
+function Whip:GetCanGiveDamageOverride()
return true
end
diff --git a/ns2/lua/Whip_Server.lua b/ns2/lua/Whip_Server.lua
index baff9d0..6ab9469 100644
--- a/ns2/lua/Whip_Server.lua
+++ b/ns2/lua/Whip_Server.lua
@@ -371,7 +371,7 @@ function Whip:OnResearchComplete(structure, researchId)
// Transform into mature whip
if structure and (structure:GetId() == self:GetId()) and (researchId == kTechId.UpgradeWhip) then
- success = self:Upgrade(kTechId.MatureWhip)
+ success = self:UpgradeToTechId(kTechId.MatureWhip)
end
diff --git a/ns2/lua/ns2.deproj b/ns2/lua/ns2.deproj
index 68869d2..7675f78 100644
--- a/ns2/lua/ns2.deproj
+++ b/ns2/lua/ns2.deproj
@@ -555,12 +555,6 @@
NS2Utility.lua
-
- Phantasm.lua
-
-
- OnosPhantasm.lua
-
Weapons\Alien\SwipeBlink.lua
@@ -1002,4 +996,25 @@
PhantomMixin.lua
+
+ SelectableMixin.lua
+
+
+ PhantomEffigy.lua
+
+
+ AttackOrderMixin.lua
+
+
+ RagdollMixin.lua
+
+
+ UpgradableMixin.lua
+
+
+ PointGiverMixin.lua
+
+
+ ScoringMixin.lua
+