Page 1 of 1

How to: Add HQ mode to your map

Posted: Wed Dec 12, 2012 3:24 pm
by Locutus
How to add HQ mode to your map


Hello community!
This tutorial will teach you how to add the mode "headquarters" to your map.

"Wait!", you might say, "what is this guy talking about? What is HQ mode?"

HQ mode is something I always wished for my maps (I got inspired by the Clone Wars game for GameCube), but it appeared that I couldn't find a tutorial for it. There was a mode called "sand castles" in the Beach Troopers mod by Maveritchell (I really love this mod), but that wasn't really what I was looking for and there where no other sources to look at. With pretty much no lua experience I started to create my own.
This is how it turned out:

In HQ mode you need to destroy the enemy HQ to win.
Each team possesses one HQ and several outposts which are destructible CPs. For each CP that gets destroyed, the HQ's energy is reduced significantly, which is why it is advisable not to attack the HQ until all hostile CPs are destroyed. You can also repair destroyed CPs, but only when the timer elapses that is triggered when a CP gets destroyed.
For each CP that is destroyed while the timer is running additional time will be added to your timer so that you cannot repair any destroyed CPs during this time.

These pictures will give you a better impression:
Hidden/Spoiler:
Image
Hidden/Spoiler:
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Hidden/Spoiler:
Image
Image
Image
Image
Image
Image
Image
As said, the outposts that have to be destroyed are CPs.
This is very useful because if one team loses a lot of cps, all of the respawning units will spawn at the remaining CPs so that they are better protected.
It can be pretty challenging to win in SP, but in MP, this mode is the real fun. Try it out;)
(Note that the map shown on the screenies turned out to be way too big and chaotic and is also very close to ZEs object limit. It was one of my first maps, too. If you want to play it anyways or take a look at the complete source files, you can find a download link at the end of this post.)



Instructions:

If you want to add this mode to your map, here's what you need to do.
I expect you to have basic modding knowledge and you should know how to add new modes to your map.
(Scroll down to the end of this post to download everything you need to add HQ mode to your map.)


1. The whole scripting part is done in the "ObjectiveHQ.lua".
I don't recommend you to change anything in here unless you know what you're doing.
I made a couple of comments, but still ... This is what the lua contains:
Hidden/Spoiler:
[code]-- ////////////////// HQ SCRIPT \\\\\\\\\\\\\\\\\\
-- \\\\\\\\\\\\\ Created by Locutus //////////////

ScriptCB_DoFile("Objective")

ObjectiveHQ = Objective:New {}


--Time limit (must not be displayed due to visible timer in SP)
function ObjectiveHQ:GetGameTimeLimit()
return 0
end


--When time elapses (will never be triggered with current code setup)
function ObjectiveHQ:GameOptionsTimeLimitUp()
if GetObjectHealth(HQ1_Name) > GetObjectHealth(HQ1_Name) then
MissionVictory(self.teamATT)
elseif GetObjectHealth(HQ1_Name) < GetObjectHealth(HQ1_Name) then
MissionVictory(self.teamDEF)
else
MissionVictory({self.teamATT,self.teamDEF})
end
end


--Start
function ObjectiveHQ:Start()
Objective.Start(self)
ShowTeamPoints(self.teamATT, true)
ShowTeamPoints(self.teamDEF, true)
SetReinforcementCount(self.teamATT, -1)
SetReinforcementCount(self.teamDEF, -1)
SetTeamPoints(self.teamATT, CPs_Per_Team)
SetTeamPoints(self.teamDEF, CPs_Per_Team)
ScriptCB_ShowHuntScoreLimit(3)
ScriptCB_SetUberScoreLimit(CPs_Per_Team)
Player = 0
Firstspawn = true
AIGoal_Count = 0
CP_Count_T1 = CPs_Per_Team
CP_Count_T2 = CPs_Per_Team
HQ1_Healthcheck = false
HQ2_Healthcheck = false
CP_Name_T1 = ""
CP_Name_T2 = ""
a = 0
SetProperty(HQ1_Name, "MaxHealth", HQ_Energy_CP * CPs_Per_Team + HQ_Energy)
SetProperty(HQ1_Name, "CurHealth", HQ_Energy_CP * CPs_Per_Team + HQ_Energy)
SetProperty(HQ2_Name, "MaxHealth", HQ_Energy_CP * CPs_Per_Team + HQ_Energy)
SetProperty(HQ2_Name, "CurHealth", HQ_Energy_CP * CPs_Per_Team + HQ_Energy)
if ScriptCB_InMultiplayer() then
print("HQ: Energy Setup Multiplayer")
for a = 1, CPs_Per_Team do
SetProperty(CP_Prefix_Team1 .. a, "MaxHealth", CP_Energy)
SetProperty(CP_Prefix_Team1 .. a, "CurHealth", CP_Energy)
SetProperty(CP_Prefix_Team2 .. a, "MaxHealth", CP_Energy)
SetProperty(CP_Prefix_Team2 .. a, "CurHealth", CP_Energy)
end
OnCharacterSpawn(
function(character)
if Firstspawn == true then
AI_Goals()
Firstspawn = false
if GetCharacterTeam(character) == self.teamATT then
Player = self.teamATT
else
Player = self.teamDEF
end
end
end
)
else
print("HQ: Energy Setup Singleplayer")
OnCharacterSpawn(
function(character)
if Firstspawn == true then
AI_Goals()
if AI_Difficulty_Modifier ~= 0 then
SetAIDifficulty(math.ceil((AI_Difficulty_Modifier * -1)/2), AI_Difficulty_Modifier)
end
for a = 1, CPs_Per_Team do
if IsCharacterHuman(character) then
if GetCharacterTeam(character) == self.teamATT then
SetProperty(CP_Prefix_Team1 .. a, "MaxHealth", CP_Energy * CP_Energy_Player_Modifier)
SetProperty(CP_Prefix_Team1 .. a, "CurHealth", CP_Energy * CP_Energy_Player_Modifier)
SetProperty(CP_Prefix_Team2 .. a, "MaxHealth", CP_Energy)
SetProperty(CP_Prefix_Team2 .. a, "CurHealth", CP_Energy)
Player = self.teamATT
print("HQ: Energy Setup 1: CP" .. a .." energy: team 1 = " .. GetObjectHealth(CP_Prefix_Team1 .. a) .. ", team 2 = " .. GetObjectHealth(CP_Prefix_Team2 .. a))
else
SetProperty(CP_Prefix_Team1 .. a, "MaxHealth", CP_Energy)
SetProperty(CP_Prefix_Team1 .. a, "CurHealth", CP_Energy)
SetProperty(CP_Prefix_Team2 .. a, "MaxHealth", CP_Energy * CP_Energy_Player_Modifier)
SetProperty(CP_Prefix_Team2 .. a, "CurHealth", CP_Energy * CP_Energy_Player_Modifier)
Player = self.teamDEF
print("HQ: Energy Setup 2: CP" .. a .." energy: team 1 = " .. GetObjectHealth(CP_Prefix_Team1 .. a) .. ", team 2 = " .. GetObjectHealth(CP_Prefix_Team2 .. a))
end
end
Firstspawn = false
end
end
end
)
end

--Win game if HQ is killed and check whether one of the cps gets destroyed
OnObjectKill(
function(object, killer)
if GetEntityName(object) == HQ1_Name then
MissionVictory(self.teamDEF)
elseif GetEntityName(object) == HQ2_Name then
MissionVictory(self.teamATT)
else
for a = 1, CPs_Per_Team do
if GetEntityName(object) == CP_Prefix_Team1 .. a then
CP_Name_T1 = GetEntityName(object)
print("HQ: OnObjectKill_T1: " .. CP_Name_T1 .. " was destroyed.")
--[[if GetObjectTeam(killer) == GetObjectTeam(object) then
AddAssaultDestroyPoints(killer)
else
AddFlagCapturePoints(killer)
end]]
CP_Count_T1 = CP_Count_T1 - 1
Headquarters_1()
AI_Goals()
CP_Respawn()
elseif GetEntityName(object) == CP_Prefix_Team2 .. a then
CP_Name_T2 = GetEntityName(object)
print("HQ: OnObjectKill_T2: " .. CP_Name_T2 .. " was destroyed.")
--[[if GetObjectTeam(killer) == GetObjectTeam(object) then
AddAssaultDestroyPoints(killer)
else
AddFlagCapturePoints(killer)
end]]
CP_Count_T2 = CP_Count_T2 - 1
Headquarters_2()
AI_Goals()
CP_Respawn()
end
end
end
end
)

--Call repair function if a cp, well, is repaired
OnObjectRespawn(
function (object)
print("HQ: OnObjectRespawn: initialized")
GetEntityName(object)
for a = 1, CPs_Per_Team do
if GetEntityName(object) == CP_Prefix_Team1 .. a then
AI_Goals()
T1_CP_Repair()
elseif GetEntityName(object) == CP_Prefix_Team2 .. a then
AI_Goals()
T2_CP_Repair()
end
end
end
)

--Check for critical health message
OnHealthChange(
function (object)
if GetEntityName(object) == HQ1_Name then
T1_HQ_Health_Check()
elseif GetEntityName(object) == HQ2_Name then
T2_HQ_Health_Check()
end
end
)


--AI goals
function AI_Goals()
Marker_Setup()
if (CP_Count_T1 > 0 and CP_Count_T2 > 0 and Firstspawn == false) or Firstspawn == true then
print("HQ: function AI_Goals: standard setup")
ClearAIGoals(self.teamATT)
ClearAIGoals(self.teamDEF)
AIGoal_Count = 0
--Destroy CPs
for a = 1, CPs_Per_Team do
if IsObjectAlive(CP_Prefix_Team1 .. a) then
if AIGoal_Count <= 19 then
AddAIGoal(self.teamDEF, "Destroy", 100/(CPs_Per_Team*2), CP_Prefix_Team1 .. a)
AIGoal_Count = AIGoal_Count + 1
else
print("HQ: function AI_Goals: limit reached: Destroy " .. CP_Prefix_Team1 .. a .. ": AIGoal_Count: " .. AIGoal_Count - 1)
end
end
if IsObjectAlive(CP_Prefix_Team2 .. a) then
if AIGoal_Count <= 19 then
AddAIGoal(self.teamATT, "Destroy", 100/(CPs_Per_Team*2), CP_Prefix_Team2 .. a)
AIGoal_Count = AIGoal_Count + 1
else
print("HQ: function AI_Goals: limit reached: Destroy " .. CP_Prefix_Team2 .. a .. ": AIGoal_Count: " .. AIGoal_Count - 1)
end
end
end
--Do some deathmatch
if AIGoal_Count <= 18 then
AddAIGoal(self.teamATT, "Deathmatch", 100/(CPs_Per_Team*3))
AddAIGoal(self.teamDEF, "Deathmatch", 100/(CPs_Per_Team*3))
AIGoal_Count = AIGoal_Count + 2
else
print("HQ: function AI_Goals: limit reached: Deathmatch: AIGoal_Count: " .. AIGoal_Count - 1)
end
--Defend CPs
for a = 1, CPs_Per_Team do
if IsObjectAlive(CP_Prefix_Team1 .. a) then
if AIGoal_Count <= 19 then
AddAIGoal(self.teamDEF, "Defend", 100/(CPs_Per_Team*6), CP_Prefix_Team2 .. a)
AIGoal_Count = AIGoal_Count + 1
else
print("HQ: function AI_Goals: limit reached: Defend " .. CP_Prefix_Team1 .. a .. ": AIGoal_Count: " .. AIGoal_Count - 1)
end
end
if IsObjectAlive(CP_Prefix_Team2 .. a) then
if AIGoal_Count <= 19 then
AddAIGoal(self.teamATT, "Defend", 100/(CPs_Per_Team*6), CP_Prefix_Team1 .. a)
AIGoal_Count = AIGoal_Count + 1
else
print("HQ: function AI_Goals: limit reached: Defend " .. CP_Prefix_Team2 .. a .. ": AIGoal_Count: " .. AIGoal_Count - 1)
end
end
end
--Destroy/defend HQ if only a few cps left
if AIGoal_Count <= 17 then
if CP_Count_T1 <= 2 then
AddAIGoal(self.teamATT, "Defend", 100/(CPs_Per_Team*2), HQ1_Name)
AddAIGoal(self.teamDEF, "Destroy", 100/(CPs_Per_Team*4), HQ1_Name)
AIGoal_Count = AIGoal_Count + 2
end
if CP_Count_T1 <= 2 and AIGoal_Count <= 17 then
AddAIGoal(self.teamATT, "Destroy", 100/(CPs_Per_Team*4), HQ2_Name)
AddAIGoal(self.teamDEF, "Defend", 100/(CPs_Per_Team*2), HQ2_Name)
AIGoal_Count = AIGoal_Count + 2
end
else
print("HQ: function AI_Goals: limit reached: HQ: AIGoal_Count: " .. AIGoal_Count - 1)
end
--No CPs both
end
if CP_Count_T1 == 0 and CP_Count_T2 == 0 then
print("HQ: function AI_Goals: attack HQ both")
ClearAIGoals(self.teamATT)
ClearAIGoals(self.teamDEF)
AIGoal_Count = 6
AddAIGoal(self.teamATT, "Destroy", 100, HQ2_Name)
AddAIGoal(self.teamATT, "Defend", 100/(CPs_Per_Team), HQ1_Name)
AddAIGoal(self.teamATT, "Deathmatch", 100/(CPs_Per_Team*3))
AddAIGoal(self.teamDEF, "Destroy", 100, HQ1_Name)
AddAIGoal(self.teamDEF, "Defend", 100/(CPs_Per_Team), HQ2_Name)
AddAIGoal(self.teamDEF, "Deathmatch", 100/(CPs_Per_Team*3))
for a = 1, CPs_Per_Team do
if AIGoal_Count <= 19 then
if IsObjectAlive(CP_Prefix_Team1 .. a) then
AddAIGoal(self.teamATT, "Defend", 100/(CPs_Per_Team*2), CP_Prefix_Team1 .. a)
AIGoal_Count = AIGoal_Count + 4
end
if IsObjectAlive(CP_Prefix_Team2 .. a) then
AddAIGoal(self.teamDEF, "Defend", 100/(CPs_Per_Team*2), CP_Prefix_Team2 .. a)
AIGoal_Count = AIGoal_Count + 1
end
else
print("HQ: function AI_Goals: limit reached: Defend " .. CP_Prefix_Team1 .. a .. ", " .. CP_Prefix_Team2 .. a .. ": AIGoal_Count: " .. AIGoal_Count - 1)
end
end
--No CPs team 1
elseif CP_Count_T1 == 0 then
print("HQ: function AI_Goals: attack HQ defender")
ClearAIGoals(self.teamDEF)
AIGoal_Count = AIGoal_Count/2 + 4
AddAIGoal(self.teamDEF, "Destroy", 100, HQ1_Name)
AddAIGoal(self.teamDEF, "Defend", 100/(CPs_Per_Team), HQ2_Name)
AddAIGoal(self.teamDEF, "Deathmatch", 100/(CPs_Per_Team*2))
for a = 1, CPs_Per_Team do
if AIGoal_Count <= 19 then
if IsObjectAlive(CP_Prefix_Team2 .. a) then
AddAIGoal(self.teamDEF, "Defend", 100/(CPs_Per_Team*2), CP_Prefix_Team2 .. a)
AIGoal_Count = AIGoal_Count + 1
end
else
print("HQ: function AI_Goals: limit reached: Defend " .. CP_Prefix_Team2 .. a .. ": AIGoal_Count: " .. AIGoal_Count - 1)
end
end
--No CPs team 2
elseif CP_Count_T2 == 0 then
print("HQ: function AI_Goals: attack HQ attacker")
ClearAIGoals(self.teamATT)
AIGoal_Count = AIGoal_Count/2 + 4
AddAIGoal(self.teamATT, "Destroy", 100, HQ2_Name)
AddAIGoal(self.teamATT, "Defend", 100/(CPs_Per_Team), HQ1_Name)
AddAIGoal(self.teamATT, "Deathmatch", 100/(CPs_Per_Team*2))
for a = 1, CPs_Per_Team do
if AIGoal_Count <= 19 then
if IsObjectAlive(CP_Prefix_Team1 .. a) then
AddAIGoal(self.teamATT, "Defend", 100/(CPs_Per_Team*2), CP_Prefix_Team1 .. a)
AIGoal_Count = AIGoal_Count + 1
end
else
print("HQ: function AI_Goals: limit reached: Defend " .. CP_Prefix_Team1 .. a .. ": AIGoal_Count: " .. AIGoal_Count - 1)
end
end
end
print("HQ: function AI_Goals: final AIGoal_Count: " .. AIGoal_Count - 1)
end


--Handle map markers
--Note that the markers behave differently depending on if the timer is running. Important for MP because timer doesn't work there
function Marker_Setup()
print("HQ: function Marker_Setup: initialized")
MapRemoveEntityMarker(HQ1_Name)
MapRemoveEntityMarker(HQ2_Name)
if CP_Count_T1 == 0 then
MapAddEntityMarker(HQ1_Name, "hud_objective_icon_circle", 0.1, self.teamDEF, "RED", true, true, true)
end
if CP_Count_T2 == 0 then
MapAddEntityMarker(HQ2_Name, "hud_objective_icon_circle", 0.1, self.teamATT, "RED", true, true, true)
end
for a = 1, CPs_Per_Team do
MapRemoveEntityMarker(CP_Prefix_Team1 .. a)
MapRemoveEntityMarker(CP_Prefix_Team1 .. a)
MapRemoveEntityMarker(CP_Prefix_Team2 .. a)
MapRemoveEntityMarker(CP_Prefix_Team2 .. a)
if IsObjectAlive(CP_Prefix_Team1 .. a) then
MapAddEntityMarker(CP_Prefix_Team1 .. a, "hud_objective_icon_circle", 3.0, self.teamDEF, "RED", true, true, true)
else
MapAddEntityMarker(CP_Prefix_Team1 .. a, "hud_objective_icon_circle", 3.0, self.teamATT, "BLUE", false, false, false)
if Timer_Running_T1 == true then
MapRemoveEntityMarker(CP_Prefix_Team1 .. a)
MapAddEntityMarker(CP_Prefix_Team1 .. a, "hud_objective_icon_circle", 4.8, self.teamATT, "BLUE", false)
end
end
if IsObjectAlive(CP_Prefix_Team2 .. a) then
MapAddEntityMarker(CP_Prefix_Team2 .. a, "hud_objective_icon_circle", 3.0, self.teamATT, "RED", true, true, true)
else
MapAddEntityMarker(CP_Prefix_Team2 .. a, "hud_objective_icon_circle", 3.0, self.teamDEF, "BLUE", false, false, false)
if Timer_Running_T2 == true then
MapRemoveEntityMarker(CP_Prefix_Team2 .. a)
MapAddEntityMarker(CP_Prefix_Team2 .. a, "hud_objective_icon_circle", 4.8, self.teamDEF, "BLUE", false)
end
end
end
end


--Display points for destruction
function Headquarters_1()
print("HQ: function Headquarters_1: CP_Count_T1=" .. CP_Count_T1 .. ", CP_Count_T2=" ..CP_Count_T2)
if CP_Count_T1 > 0 then
ShowMessageText(Text_CP_Destroyed_T1, self.teamATT)
ShowMessageText(Text_CP_Destroyed_T2, self.teamDEF)
else
ShowMessageText(Text_CP_All_Dead_T1, self.teamATT)
ShowMessageText(Text_CP_All_Dead_T2, self.teamDEF)
end
AddTeamPoints(self.teamATT,-1)
HQ1_Min_Energy()
if GetTimerValue(respawn_timer_t1) then
print("HQ: function Headquarters_1: timer time = " .. GetTimerValue(respawn_timer_t1))
end
end
function Headquarters_2()
print("HQ: function Headquarters_2: CP_Count_T1=" .. CP_Count_T1 .. ", CP_Count_T2=" ..CP_Count_T2)
if CP_Count_T2 > 0 then
ShowMessageText(Text_CP_Destroyed_T1, self.teamDEF)
ShowMessageText(Text_CP_Destroyed_T2, self.teamATT)
else
ShowMessageText(Text_CP_All_Dead_T1, self.teamDEF)
ShowMessageText(Text_CP_All_Dead_T2, self.teamATT)
end
AddTeamPoints(self.teamDEF,-1)
HQ2_Min_Energy()
if GetTimerValue(respawn_timer_t2) then
print("HQ: function Headquarters_2: timer time = " .. GetTimerValue(respawn_timer_t2))
end
end

--Correct values in case cp gets repaired
function T1_CP_Repair()
print("HQ: function T1_CP_Repair: initialized.")
AddTeamPoints(self.teamATT,1)
CP_Count_T1 = CP_Count_T1 + 1
ShowMessageText(Text_CP_Repaired_T1, self.teamATT)
ShowMessageText(Text_CP_Repaired_T2, self.teamDEF)
SetProperty(HQ1_Name, "MaxHealth", GetObjectHealth(HQ1_Name) + HQ_Energy_CP)
SetProperty(HQ1_Name, "CurHealth", GetObjectHealth(HQ1_Name) + HQ_Energy_CP)
print("HQ: function T1_CP_Repair: CP_Count_T1 = " .. CP_Count_T1 .. ", CP_Count_T2 = " .. CP_Count_T2 .. ", hq1_CurHealth = " .. GetObjectHealth(HQ1_Name))
end
function T2_CP_Repair()
print("HQ: function T2_CP_Repair: initialized.")
AddTeamPoints(self.teamDEF,1)
CP_Count_T2 = CP_Count_T2 + 1
ShowMessageText(Text_CP_Repaired_T1, self.teamDEF)
ShowMessageText(Text_CP_Repaired_T2, self.teamATT)
SetProperty(HQ2_Name, "MaxHealth", GetObjectHealth(HQ2_Name) + HQ_Energy_CP)
SetProperty(HQ2_Name, "CurHealth", GetObjectHealth(HQ2_Name) + HQ_Energy_CP)
print("HQ: function T2_CP_Repair: CP_Count_T1 = " .. CP_Count_T1 .. ", CP_Count_T2 = " .. CP_Count_T2 .. ", hq2_CurHealth = " .. GetObjectHealth(HQ2_Name))
end

--Reduce HQ health for each CP that gets destroyed
function HQ1_Min_Energy()
print("HQ: function HQ1_Min_Energy: initialized. HQ1_CurHealth = " .. GetObjectHealth(HQ1_Name))
if GetObjectHealth(HQ1_Name) > HQ_Energy_CP + HQ_Energy_Min then
print("HQ: function HQ1_Min_Energy(): GetObjectHealth(hq1) > 11000")
SetProperty(HQ1_Name, "MaxHealth", GetObjectHealth(HQ1_Name) - HQ_Energy_CP)
SetProperty(HQ1_Name, "CurHealth", GetObjectHealth(HQ1_Name) - HQ_Energy_CP)
elseif GetObjectHealth(HQ1_Name) > HQ_Energy_Min and GetObjectHealth(HQ1_Name) < HQ_Energy_CP then
print("HQ: function HQ1_Min_Energy(): GetObjectHealth(hq1) > 1000 and GetObjectHealth(hq1) < 10000")
SetProperty(HQ1_Name, "MaxHealth", HQ_Energy_Min)
SetProperty(HQ1_Name, "CurHealth", HQ_Energy_Min)
else
print("HQ: function HQ1_Min_Energy(): HQ has already less than 1000")
end
print("HQ: function HQ1_Min_Energy: finished. HQ1_CurHealth = " .. GetObjectHealth(HQ1_Name))
end
function HQ2_Min_Energy()
print("HQ: function HQ2_Min_Energy: initialized. HQ2_CurHealth = " .. GetObjectHealth(HQ2_Name))
if GetObjectHealth(HQ2_Name) > HQ_Energy_CP + HQ_Energy_Min then
print("HQ: function HQ2_Min_Energy: HQ2_Min_Energy: GetObjectHealth(hq2) > 10000")
SetProperty(HQ2_Name, "MaxHealth", GetObjectHealth(HQ2_Name) - HQ_Energy_CP)
SetProperty(HQ2_Name, "CurHealth", GetObjectHealth(HQ2_Name) - HQ_Energy_CP)
elseif GetObjectHealth(HQ2_Name) > HQ_Energy_Min and GetObjectHealth(HQ2_Name) < HQ_Energy_CP then
print("HQ: function HQ2_Min_Energy: HQ2_Min_Energy: GetObjectHealth(hq2) > 1000 and GetObjectHealth(hq2) < 10000")
SetProperty(HQ2_Name, "MaxHealth", HQ_Energy_Min)
SetProperty(HQ2_Name, "CurHealth", HQ_Energy_Min)
else
print("HQ: function HQ2_Min_Energy(): HQ has already less than HQ_Energy_Min (1000)")
end
print("HQ: function HQ2_Min_Energy: finished. HQ2_CurHealth = " .. GetObjectHealth(HQ2_Name))
end

--Warn players if health of their HQ is low
function T1_HQ_Health_Check()
if GetObjectHealth(HQ1_Name) < HQ_Energy_Warning and HQ1_Healthcheck == false then
print("HQ: function T1_HQ_Health_Check: HQ1_Healthcheck = true")
ShowMessageText(Text_HQ_Damaged_T1, self.teamATT)
ShowMessageText(Text_HQ_Damaged_T2, self.teamDEF)
HQ1_Healthcheck = true
elseif GetObjectHealth(HQ1_Name) > HQ_Energy_Warning and HQ1_Healthcheck == true then
print("HQ: function T1_HQ_Health_Check: HQ1_Healthcheck = false")
HQ1_Healthcheck = false
end
end
function T2_HQ_Health_Check()
if GetObjectHealth(HQ2_Name) < HQ_Energy_Warning and HQ2_Healthcheck == false then
print("HQ: function T2_HQ_Health_Check: HQ2_Healthcheck = true")
ShowMessageText(Text_HQ_Damaged_T1, self.teamDEF)
ShowMessageText(Text_HQ_Damaged_T2, self.teamATT)
HQ2_Healthcheck = true
elseif GetObjectHealth(HQ2_Name) > HQ_Energy_Warning and HQ2_Healthcheck == true then
print("HQ: function T2_HQ_Health_Check: HQ2_Healthcheck = false")
HQ2_Healthcheck = false
end
end

--Prevent the player from immediately rebuilding a destroyed cp
function CP_Respawn()
print("HQ: function CP_Respawn: initialized")
if CP_Name_T1 ~= "" then
print("HQ: function CP_RespawnT1: " .. CP_Name_T1 .. " is now invisible")
SetProperty(CP_Name_T1, "AINoRepair", 1)
SetProperty(CP_Name_T1, "MaxHealth", 1e+37)
SetProperty(CP_Name_T1, "IsVisible", 0)
SetProperty(CP_Name_T1, "CurHealth", 0)
print("HQ: function CP_RespawnT1: create timer")
respawn_timer_t1 = CreateTimer("respawn_timer_t1")
if not GetTimerValue(respawn_timer_t1) then
timervalue_t1 = Timervalue
else
timervalue_t1 = GetTimerValue(respawn_timer_t1) + Timervalue
end
SetTimerValue(respawn_timer_t1 , timervalue_t1)
if Player == self.teamATT and not ScriptCB_InMultiplayer() then
ShowTimer(respawn_timer_t1)
end
StartTimer(respawn_timer_t1)
Timer_Running_T1 = true
for a = 1, CPs_Per_Team do
if not IsObjectAlive(CP_Prefix_Team1 .. a) then
SetProperty(CP_Prefix_Team1 .. a, "AINoRepair", 1)
SetProperty(CP_Prefix_Team1 .. a, "MaxHealth", 1e+37)
SetProperty(CP_Prefix_Team1 .. a, "IsVisible", 0)
SetProperty(CP_Prefix_Team1 .. a, "CurHealth", 0)
end
end
OnTimerElapse(
function(timer)
print("HQ: function CP_RespawnT1: timer elapsed")
Timer_Running_T1 = false
Marker_Setup()
for a = 1, CPs_Per_Team do
SetProperty(CP_Prefix_Team1 .. a, "IsVisible", 1)
SetProperty(CP_Prefix_Team1 .. a, "AINoRepair", 0)
SetProperty(CP_Prefix_Team1 .. a, "MaxHealth", CP_Energy)
if not IsObjectAlive(CP_Prefix_Team1 .. a) then
SetProperty(CP_Prefix_Team1 .. a, "CurHealth", 0)
end
end
if Player == self.teamATT and GetTimerValue(respawn_timer_t1) < 0.1 then
ShowTimer(nil)
end
DestroyTimer(timer)
end,
respawn_timer_t1
)
CP_Name_T1 = ""
Marker_Setup()
elseif CP_Name_T2 ~= "" then
print("HQ: function CP_RespawnT2: " .. CP_Name_T2 .. " is now invisible")
SetProperty(CP_Name_T2, "AINoRepair", 1)
SetProperty(CP_Name_T2, "MaxHealth", 1e+37)
SetProperty(CP_Name_T2, "IsVisible", 0)
SetProperty(CP_Name_T2, "CurHealth", 0)
print("HQ: function CP_RespawnT2: create timer")
respawn_timer_t2 = CreateTimer("respawn_timer_t2")
if not GetTimerValue(respawn_timer_t2) then
timervalue_t2 = Timervalue
else
timervalue_t2 = GetTimerValue(respawn_timer_t2) + Timervalue
end
SetTimerValue(respawn_timer_t2 , timervalue_t2)
if Player == self.teamDEF and not ScriptCB_InMultiplayer() then
ShowTimer(respawn_timer_t2)
end
StartTimer(respawn_timer_t2)
Timer_Running_T2 = true
for a = 1, CPs_Per_Team do
if not IsObjectAlive(CP_Prefix_Team2 .. a) then
SetProperty(CP_Prefix_Team2 .. a, "AINoRepair", 1)
SetProperty(CP_Prefix_Team2 .. a, "MaxHealth", 1e+37)
SetProperty(CP_Prefix_Team2 .. a, "IsVisible", 0)
SetProperty(CP_Prefix_Team2 .. a, "CurHealth", 0)
end
end
OnTimerElapse(
function(timer)
print("HQ: function CP_RespawnT2: timer elapsed")
Timer_Running_T2 = false
Marker_Setup()
for a = 1, CPs_Per_Team do
SetProperty(CP_Prefix_Team2 .. a, "IsVisible", 1)
SetProperty(CP_Prefix_Team2 .. a, "AINoRepair", 0)
SetProperty(CP_Prefix_Team2 .. a, "MaxHealth", CP_Energy)
if not IsObjectAlive(CP_Prefix_Team2 .. a) then
SetProperty(CP_Prefix_Team2 .. a, "CurHealth", 0)
end
end
if Player == self.teamDEF and GetTimerValue(respawn_timer_t2) < 0.1 then
ShowTimer(nil)
end
DestroyTimer(timer)
end,
respawn_timer_t2
)
CP_Name_T2 = ""
Marker_Setup()
end
end

end
[/code]
2. Copy the ObjectiveHQ.lua from the downloaded package and paste it into your ...\data_MAP\common\scripts folder.

3. Goto ...\data_MAP\Common and open mission.req. It should look similar to this:
Hidden/Spoiler:
[code]ucft
{
REQN
{
"config"
"ingame_movies"
}

REQN
{
"script"
"setup_teams"
"gametype_conquest"
"gametype_capture"
"Objective"
"MultiObjectiveContainer"
"ObjectiveCTF"
"ObjectiveAssault"
"ObjectiveSpaceAssault"
"ObjectiveConquest"
"ObjectiveOneFlagCTF"
"SoundEvent_ctf"
"ObjectiveGoto"
"LinkedShields"
"LinkedDestroyables"
"LinkedTurrets"
"Ambush"
"PlayMovieWithTransition"
}

REQN
{
"lvl"
"MAPg_con"
"MAPc_con"
}
}[/code]
Now, add "ObjectiveHQ" in the script sections. The final result should look similar to this:
Hidden/Spoiler:
[code]ucft
{
REQN
{
"config"
"ingame_movies"
}

REQN
{
"script"
"setup_teams"
"gametype_conquest"
"gametype_capture"
"Objective"
"MultiObjectiveContainer"
"ObjectiveCTF"
"ObjectiveAssault"
"ObjectiveSpaceAssault"
"ObjectiveConquest"
"ObjectiveHQ"
"ObjectiveOneFlagCTF"
"SoundEvent_ctf"
"ObjectiveGoto"
"LinkedShields"
"LinkedDestroyables"
"LinkedTurrets"
"Ambush"
"PlayMovieWithTransition"
}

REQN
{
"lvl"
"MAPg_con"
"MAPc_con"
}
}[/code]
4. Now to the rather difficult part: HQ mode requires a new script for that mode (obviously), but also a new layer in ZE.
With the 1.3 patch [RDH]Zerted provided a couple of very useful additions. You can use whichever suffix you want that came with the latest release:
Hidden/Spoiler:
Added support for game mode: tdm
Added support for game mode: xl
Added support for game mode: obj
Added support for game mode: uber
Added support for game mode: bf1
Added support for game mode: holo
Added support for game mode: ord66
Added support for game mode: dm
Added support for game mode: space
Added support for game mode: c1
Added support for game mode: c2
Added support for game mode: c3
Added support for game mode: c4
Added support for game mode: hctf
Added support for game mode: vhcon
Added support for game mode: vhtdm
Added support for game mode: vhctf
Added support for game mode: avh
Added support for game mode: lms
Added support for game mode: vh
Added support for game mode: race
Added support for game mode: koh
Added support for game mode: tdf
Added support for game mode: surv
Added support for game mode: rpg
Added support for game mode: wav
Added support for game mode: ctrl
Added support for game mode: seige
Added support for game mode: siege
Added support for game mode: jhu
Added support for game mode: wea
Added support for game mode: ins
Create a script for that mode and add it to your mission.req, just as you have done before.
But this time add it the "lvl" section.
Hidden/Spoiler:
[code] REQN
{
"lvl"
"MAPg_con"
"MAPc_con"
"MAP*era*_YourSuffix"
}[/code]
5.) Add the mode to the addme.lua. I have marked everthing new with --!!!.
Hidden/Spoiler:
[code]--Search through the missionlist to find a map that matches mapName,
--then insert the new flags into said entry.
--Use this when you know the map already exists, but this content patch is just
--adding new gamemodes (otherwise you should just add whole new entries to the missionlist)
function AddNewGameModes(missionList, mapName, newFlags)
for i, mission in missionList do
if mission.mapluafile == mapName then
for flag, value in pairs(newFlags) do
mission[flag] = value
end
end
end
end




--insert totally new maps here:
local sp_n = 0
local mp_n = 0
sp_n = table.getn(sp_missionselect_listbox_contents)

sp_missionselect_listbox_contents[sp_n+1] = {
isModLevel = 1,
mapluafile = "MAP%s_%s",
era_g = 1,
era_c = 1,
mode_HQ_g = 1 , --!!!Replace HQ with your suffix. Keep in mind that HQ doesn't exist as a mode.
mode_con_g = 1,
mode_con_c = 1,
change = {
mode_HQ = --!!!Once again, replace HQ with your suffix. The next three lines will change the ingame mode name, icon and description.
{ name="HQ",
icon="mode_icon_holo",
about="Each team tries to destroy the opposing HQ."
},
},
}
mp_n = table.getn(mp_missionselect_listbox_contents)
mp_missionselect_listbox_contents[mp_n+1] = sp_missionselect_listbox_contents[sp_n+1]

-- associate this mission name with the current downloadable content directory
-- (this tells the engine which maps are downloaded, so you need to include all new mission lua's here)
-- first arg: mapluafile from above
-- second arg: mission script name
-- third arg: level memory modifier. the arg to LuaScript.cpp: DEFAULT_MODEL_MEMORY_PLUS(x)

AddDownloadableContent("MAP","MAPg_HQ",4) --!!!I am sure you know what to do here;)
AddDownloadableContent("MAP","MAPg_con",4)
AddDownloadableContent("MAP","MAPc_con",4)

-- all done
newEntry = nil
n = nil

-- Now load our core.lvl into the shell to add our localize keys
ReadDataFile("..\\..\\addon\\MAP\\data\\_LVL_PC\\core.lvl")[/code]
6. Add your layer to ZE. How to add a new layer for hunt mode is described in this tutorial - you can do this for HQ accordingly.


7. In the new layer, you need to place the HQ relevant objects. These are the destructible outposts (CPs) and the HQs. First of all, copy the ODFs, meshes and textures from the package to your world odf/msh folders (downloadable package at the end of this post). Of course you can use your own meshes and odf files, but this makes it easy to start with.
Each team needs exactly one HQ, preferably placed on the opposite sides of your map.
I recommend to place the HQ in a protected position so that it can't be shot over half the map (same for the CPs). The HQ ODF is cis_bldg_hq_building.odf. Give each HQ a unique name in ZE.
Next, you need to place the CPs (a_nic_des_cp.odf) on the map. You can place as many as you want, but keep mind that the game doesn't support more than 16 CPs (remember the HQs which take already 2 CP "slots"). Also note that you have to place the same amount of CPs for each team.
When placing the CPs you need to set up a naming scheme starting with number 1 for each team, for example team1_cp1, team1_cp2, and so on.


8. Now, you need to modify your MAP*era*_YourSuffix.lua script.
The first section including ScriptPostLoad section should look like this (make sure to adjust the variables, especially HQ and CP names):
Hidden/Spoiler:
[code]--
-- HQ setup by Locutus
--

ScriptCB_DoFile("setup_teams")
ScriptCB_DoFile("ObjectiveHQ")

--randomize sides (this is optional, delete everything but the else part if you don't want random sides)
--NOTE: This doesn't seem to work perfectly, sides are random in multiplayer too (NO CRASH!)
if not ScriptCB_InMultiplayer() then
ALL = math.random(1,2)
IMP = 3 - ALL
else
IMP = 1
ALL = 2
end


function ScriptPostLoad()

HQ = ObjectiveHQ:New{teamATT = 1, teamDEF = 2, textATT = "game.modes.HQ1", textDEF = "game.modes.HQ2",}

--Variable setup: You can adjust these values according to your preferences
CPs_Per_Team = 6 --Self-explanatory. Same number must be placed in ZE (without HQ)
CP_Prefix_Team1 = "t1_cp" --You'll need to set up a naming scheme in ZE for your cps starting with number 1 for each team >>
CP_Prefix_Team2 = "t2_cp" -->>example would be team1_cp1, team1_cp2, and so on. Leave out the cp numbers here
CP_Energy = 1000 --You can define the energy of the cps here
CP_Energy_Player_Modifier = 0.6 --In SP your command posts will have less energy than the ones from the enemy. Set this to 1 if you don't want any changes
HQ1_Name = "hq1" --Name of the HQ1 object in ZE
HQ2_Name = "hq2" --Name of the HQ2 object in ZE
HQ_Energy = 3000 --Energy of the HQ without cps
HQ_Energy_CP = 15000 --This is the additional energy for the HQ for _each_ cp
HQ_Energy_Min = 1000 --Minimal value of energy your HQ can reach when cps are destroyed
HQ_Energy_Warning = 1500 --Warn players if their HQ got less energy than the number specified
AI_Difficulty_Modifier = 3 --In SP allied units will be weaker than enemy bots. Maximum is 10, make sure to use whole numbers. Set this to 0 if you don't want any changes
Timervalue = 60 --Sets the amount of time that will be added to the timer when a cp gets destroyed
Text_CP_Destroyed_T1 = "level.MAP.hints.t1_cp_destroyed" --One of our command posts has been destroyed!
Text_CP_Destroyed_T2 = "level.MAP.hints.t2_cp_destroyed" --We have destroyed one of the enemy's command posts!
Text_CP_All_Dead_T1 = "level.MAP.hints.t1_cp_all_dead" --All of our command posts are destroyed!
Text_CP_All_Dead_T2 = "level.MAP.hints.t2_cp_all_dead" --We have destroyed all hostile command posts!
Text_CP_Repaired_T1 = "level.MAP.hints.t1_cp_repaired" --One of our command posts has been repaired!
Text_CP_Repaired_T2 = "level.MAP.hints.t2_cp_repaired" --One of the enemy's command posts has been repaired!
Text_HQ_Damaged_T1 = "level.MAP.hints.t1_hq_damaged" --Our Headquarters is heavily damaged!
Text_HQ_Damaged_T2 = "level.MAP.hints.t2_hq_damaged" --The enemy's Headquarters is heavily damaged!
--Localization only:
--"entity.cis.bldg_hq_building" --Headquarters
--"entity.a.map_des_cp" --Outpost
--"game.modes.HQ1" --Destroy the hostile HQ to win. For each CP that gets destroyed, the HQ's energy is reduced significantly, which is why it is advisable not to attack the HQ until all hostile CPs are destroyed>>
--"game.modes.HQ2" -->>CPs can only be repaired when the timer elapses that is triggered when a CP gets destroyed.


HQ:Start()


EnableSPHeroRules()

end
[/code]
You can copy your ScriptInit section from any conquest lua script, but don't forget to load the correct layer:
Hidden/Spoiler:
[code]SetSpawnDelay(10.0, 0.25)
ReadDataFile("dc:MAP\\MAP.lvl", "MAP_Layername")[/code]
9. You are done! Munge your map and play!




A couple of additional notes:
Feel free to use and modify this script as you like. You don't need to give credits or ask me for permission to use it. (I wouldn't mind a short message if you do so, though.)
I know that my script isn't perfect and that you could improve a little here and there. It just turned out to look this way with all the modifications and changes I made during the creation process. If you know how to improve it significantly, let me know.
Any and all criticism, suggestions, questions and/or feedback are welcome.

You can find the HQ mode package here: Download
If you want to play a map with HQ mode on it, download: Naboo - Inner City


Have fun!
~Locutus

Re: How to: Add HQ mode to your map

Posted: Wed Dec 12, 2012 3:48 pm
by GAB
Really cool. I might add it to my current project.

Re: How to: Add HQ mode to your map

Posted: Wed Dec 12, 2012 3:59 pm
by yuke5
GAB wrote:Really cool. I might add it to my current project.
Same.

Re: How to: Add HQ mode to your map

Posted: Wed Dec 12, 2012 5:00 pm
by Maveritchell
Nice work. Glad you at least had Sand Castles as a reference, because it was designed as an assault/HQ/siege(whatever you call it)-lite-type mode. The reason I didn't go with something more extensive is because I designed that to work as strictly a luascript add-on to preexisting maps. A mode that works in a similar fashion can be found on Archer01's map Yavin: Arroyo Pass (http://starwarsbattlefront.filefront.co ... Pass;72406), although this too was a mode built around having a map built for it. For other reference, you can look at other game types that use this "sequential-but-equal" objective type - games like MOBAs are a prime example.

Re: How to: Add HQ mode to your map

Posted: Wed Dec 12, 2012 5:45 pm
by Marth8880
Very, VERY nice work! :o :thumbs:

Just a thing...
LEO wrote:
Hidden/Spoiler:
Image
I could have sworn Königin means King, but I remembered that would be Königer, I believe... ^^

Re: How to: Add HQ mode to your map

Posted: Wed Dec 12, 2012 9:43 pm
by Locutus
@yuke5 and GAB:
I hope it's of some use for you.
If you have any questions don't hesitate to ask :)

Maveritchell wrote:A mode that works in a similar fashion can be found on Archer01's map Yavin: Arroyo Pass (http://starwarsbattlefront.filefront.co ... Pass;72406), although this too was a mode built around having a map built for it. For other reference, you can look at other game types that use this "sequential-but-equal" objective type - games like MOBAs are a prime example.
Actually I'm happy that I didn't do this.
Due to not looking at other implementations I created my very own HQ mode with it's own features like the possibility to kill the HQ without all CPs being destroyed and the timer-combined possibility.
But yeah, it was your Sand Castles mode which gave me the motivation to start this.

Marth8880 wrote:Very, VERY nice work! :o :thumbs:

Just a thing...
LEO wrote:
Hidden/Spoiler:
Image
I could have sworn Königin means King, but I remembered that would be Königer, I believe... ^^
Hehe, your translation is close, König means king and Königin = queen^^
(Quotes are buggy again, LEO didn't post that picture;))