-------------------------------------------------------------- -- L_CASTLE_DEFENSE_TST_SERVER.lua -- Server side test script for Castle Defense prototyping. -- created abeechler ... 8/5/11 -------------------------------------------------------------- -------------------------------------------------------------- -- Includes -------------------------------------------------------------- require('ai/ACT/L_ACT_GENERIC_ACTIVITY_MGR') local scorePrintDelay = 1 local scoreCalcDelay = 1 local scoreLimit = 3960 local bGameOver = false local bQueueingChar = true local cannonballObjID = 16748 local teamScoreTable = {} local teamTable = {{}, {}} -- A nested table of teams local playerTeamTable = {} -- A player keyed table mapping player IDs to their mapped team value ---------------------------------------------------------------- -- User Config local variables ---------------------------------------------------------------- local gConstants = { acceptedDelay = 2, -- how long to wait after all the players have accepted before starting the game. startDelay = 2, -- how long to wait after all the players have accepted before starting the game. } ---------------------------------------------------------------- -- Script only local variables ---------------------------------------------------------------- local gGamestate = { tPlayers = {}, -- players who have entered the game tQueuedPlayers = {}, iNumberOfPlayers = 0 -- number of players given from ZoneLoadedInfo } ---------------------------------------------------------------- -- Player has loaded into the map ---------------------------------------------------------------- function onPlayerLoaded(self, msg) local playerID = msg.playerID if not playerID then return end UpdatePlayer(self, playerID) -- Set player stats to max playerID:SetHealth{health = playerID:GetMaxHealth{}.health} playerID:SetArmor{armor = playerID:GetMaxArmor{}.armor} playerID:SetImagination{imagination = playerID:GetMaxImagination{}.imagination} bQueueingChar = true table.insert(gGamestate.tPlayers, playerID:GetID()) table.insert(gGamestate.tQueuedPlayers, playerID:GetID()) end function onPlayerAddedToLocalTeam(self, msg) local playerID = msg.playerID local iTeamNum = msg.iTeamID + 1 -- Assign to a valid team table if(teamTable[iTeamNum] == nil) then teamTable[iTeamNum] = {} end table.insert(teamTable[iTeamNum], playerID) -- Establish a proper player mapping on the playerTeamTable playerTeamTable[playerID] = iTeamNum -- Spawn at team appropriate grouping SetPlayerSpawnPoints(self, playerID) end ---------------------------------------------------------------- -- Player has exited the map ---------------------------------------------------------------- function onPlayerExit(self, msg) local playerNum = 0 --print('player ' .. msg.playerID:GetName().name .. ' has exited') self:FireEventClientSide{args = "StopCD_UI", rerouteID = msg.playerID} for i = 1, table.maxn(gGamestate.tPlayers) do if gGamestate.tPlayers[i] == msg.playerID:GetID() then playerNum = i end end if playerNum ~= 0 then table.remove(gGamestate.tPlayers, playerNum) end playerNum = 0 for k,v in ipairs(gGamestate.tQueuedPlayers) do if msg.playerID:GetID() == v then playerNum = k end end if playerNum ~= 0 then table.remove(gGamestate.tQueuedPlayers, playerNum) end UpdatePlayer(self, msg.playerID, true) local numPlayers = self:GetNetworkVar('NumberOfPlayers') self:SetNetworkVar('NumberOfPlayers', numPlayers - 1) if(bQueueingChar) then ProcessPlayerCount(self) end end function onZoneLoadedInfo(self, msg) self:SetNetworkVar('NumberOfPlayers', msg.maxPlayersSoft) end ---------------------------------------------------------------- -- Sent a player when they are restarting into a new game either -- by choice, or by force. ---------------------------------------------------------------- function processReturningPlayer(self, player) RemoveActivityItems(player) teamScoreTable = {} bQueueingChar = true -- Rebuild the walls for i, tTab in ipairs(teamTable) do local wallObjects = self:GetObjectsInGroup{group = "Team" .. tostring(i), ignoreSpawners = true}.objects for i, wall in ipairs(wallObjects) do -- If the wallState is '2', it is built local wallState = wall:GetRebuildState().iState if(wall:Exists() and (wallState ~= 2)) then wall:FireEvent{senderID=self, args="Rebuild"} end end end table.insert(gGamestate.tQueuedPlayers, player:GetID()) self:FireEventClientSide{args = "StopCD_UI", rerouteID = player} self:FireEventClientSide{args = "Show_Startup", rerouteID = player} RestartPlayer(self, player) end ---------------------------------------------------------------- -- This is called when players hit the UI to exit or stop the game. ---------------------------------------------------------------- function baseMessageBoxRespond(self, msg, newMsg) if(msg.identifier == "Exit_Question" ) and msg.iButton == 1 then ResetStats(msg.sender) RemoveActivityItems(msg.sender) -- Send player to a specific location msg.sender:TransferToLastNonInstance{ playerID = msg.sender, bUseLastPosition = true} elseif(msg.identifier == "Win_Window") then if(msg.iButton == 1) then processReturningPlayer(self, msg.sender) elseif(msg.iButton == 0) then local tCountdown = self:GetVar("tCountdown") if(tCountdown > 0) then RemoveActivityItems(msg.sender) ResetStats(msg.sender) -- Send player to a specific location msg.sender:TransferToLastNonInstance{ playerID = msg.sender, bUseLastPosition = true} end end elseif msg.identifier == "ActivityCloseButtonPressed" and msg.iButton == -1 then -- When the player hits the activity close button, the 'x' in the top right ExitBox(self, msg.sender) end end function ExitBox(self, player) if player:Exists() then local text = "Are you sure you want to exit?" -- Display exit box player:DisplayMessageBox{bShow = true, imageID = 1, text = text, callbackClient = self, identifier = "Exit_Question"} end end function RemoveActivityItems(player) if (player:GetInvItemCount{iObjTemplate = cannonballObjID}.itemCount >= 1) then -- Take the cannonballs from the player player:RemoveItemFromInventory{iObjTemplate = cannonballObjID, iStackCount = 1} end end ---------------------------------------------------------------- -- Custom function: Checks to see if all players have accepted, -- if they have then the game is started. ---------------------------------------------------------------- function ProcessPlayerCount(self) if(table.maxn(gGamestate.tQueuedPlayers) >= self:GetNetworkVar('NumberOfPlayers')) then self:SetNetworkVar("Pop_State", true) ActivityTimerStopAllTimers(self) ActivityTimerStart(self, 'StartDelay', 3, 3) --(timerName, updateTime, stopTime) end end ---------------------------------------------------------------- -- When ActivityTimerUpdate is sent, basically when a timer hits it updateInterval. ---------------------------------------------------------------- function onActivityTimerUpdate(self, msg) if((msg.name == "printScore") and (bGameOver == false)) then for tNom, tScores in pairs(teamScoreTable) do self:FireEventClientSide{args = tNom, param2 = tScores[2]} end elseif((msg.name == "calculateScore") and (bGameOver == false)) then calculateScoreTally(self) elseif msg.name == "printTimeCountdown" then local tCountdown = self:GetVar("tCountdown") self:FireEventClientSide{args = tCountdown, param2 = 2} -- Check for the game start condition tCountdown = tCountdown - 1 if(tCountdown <= 0) then -- The countdown is over, force initiate the next game self:ActivityTimerStop(self, "printTimeCountdown") self:SetVar("tCountdown", 0) startIdlePlayers(self, msg) else -- Set the timer and continue the countdown self:SetVar("tCountdown", tCountdown) end end end ---------------------------------------------------------------- -- Iterate through the current game players, and eject any players -- currently not ready for the next game ---------------------------------------------------------------- function startIdlePlayers(self) for i, checkPID in ipairs(gGamestate.tPlayers) do -- By default, we assume the player has not readied-up local bNotQueued = true for j, waitingPID in ipairs(gGamestate.tQueuedPlayers) do if(checkPID == waitingPID) then bNotQueued = false break end end if(bNotQueued) then local idlePlayer = GAMEOBJ:GetObjectByID(checkPID) -- Our current checking playerID has not readied-up -- Close the response window idlePlayer:DisplayMessageBox{bShow = false, identifier = "Win_Window"} -- Process them as a returning player processReturningPlayer(self, idlePlayer) -- remove him from the game for idling -- ResetStats(idlePlayer) -- Close the response window -- idlePlayer:DisplayMessageBox{bShow = false, identifier = "Win_Window"} -- Send player to a specific location -- idlePlayer:TransferToLastNonInstance{playerID = idlePlayer, bUseLastPosition = true} end end end ---------------------------------------------------------------- -- When ActivityTimerDone is sent, basically when the activity timer has reached it's duration. ---------------------------------------------------------------- function onActivityTimerDone(self, msg) if msg.name == "StartDelay" then --print('adding in timers *******************************') ActivityTimerStart(self, 'PlaySpawnSound', 3, 3) --(timerName, updateTime, stopTime) -- Initiate player activity self:SetNetworkVar("Show_Countdown", true) elseif msg.name == "PlaySpawnSound" then -- Play war horn sound for k,v in ipairs(gGamestate.tPlayers) do GAMEOBJ:GetObjectByID(v):PlayNDAudioEmitter{m_NDAudioEventGUID = '{ca36045d-89df-4e96-a317-1e152d226b69}'} end StartGame(self) self:FireEventClientSide{args = "StartCD_UI"} end end function ResetStats(playerID) -- Set the player's imag, health and armor to full if playerID:Exists() then -- Set player stats to max playerID:SetHealth{health = playerID:GetMaxHealth{}.health} playerID:SetArmor{armor = playerID:GetMaxArmor{}.armor} playerID:SetImagination{imagination = playerID:GetMaxImagination{}.imagination} end end ---------------------------------------------------------------- -- Custom function: Starts the game. ---------------------------------------------------------------- function StartGame(self) for k,v in ipairs(gGamestate.tPlayers) do local playerID = GAMEOBJ:GetObjectByID(v) if not playerID then return end UpdatePlayer(self, playerID) ResetStats(playerID) if not self:GetVar('firstTimeDone') then -- Remove the activity cost from the player as they load into the map local takeCost = self:ChargeActivityCost{user = playerID}.bSucceeded end end self:SetVar('firstTimeDone', true) gGamestate.tQueuedPlayers = {} bGameOver = false bQueueingChar = false -- Initiate player activity self:SetNetworkVar("Start_Message", true) -- Start the score accumulation ActivityTimerStart(self, "calculateScore", scoreCalcDelay) ActivityTimerStart(self, "printScore", scorePrintDelay) end function RestartPlayer(self, player) if(not player:Exists()) then return end player:Resurrect() SetPlayerSpawnPoints(self, player) end function calculateScoreTally(self) for i, tTab in ipairs(teamTable) do local currentScore = 0 local teamString = "Team" .. tostring(i) if(teamScoreTable[teamString]) then currentScore = teamScoreTable[teamString][1] end local scoreTally = 0 local wallObjects = self:GetObjectsInGroup{group = teamString, ignoreSpawners = true}.objects for i, wall in ipairs(wallObjects) do -- If the wallState is '2', it is built local wallState = wall:GetRebuildState().iState if(wall:Exists() and (wallState == 2)) then scoreTally = scoreTally + 1 end end currentScore = currentScore + scoreTally local limitPercent = math.ceil(100 * (currentScore / scoreLimit)) teamScoreTable[teamString] = {currentScore, limitPercent} end winCheck(self) end function winCheck(self) if(bGameOver == true) then return end local winningTeam = {} for tNom, tScores in pairs(teamScoreTable) do if(tScores[2] >= 100) then table.insert(winningTeam, tNom) end end local numWinners = table.maxn(winningTeam) if(numWinners > 0) then local message_string = "" local bDisplayMessage = true if(numWinners > 1) then message_string = "TIE!!! Play Again?" else message_string = winningTeam[1] .. " WINS!!! Play Again?" end for i = 1, table.maxn(gGamestate.tPlayers) do local playerID = GAMEOBJ:GetObjectByID(gGamestate.tPlayers[i]) playerID:DisplayMessageBox{bShow = true, text = message_string, callbackClient = self, identifier = "Win_Window"} end bGameOver = true self:SetVar("superShotTable", {}) ActivityTimerStopAllTimers(self) -- Start a countdown for y/n responses self:SetVar("tCountdown", 30) ActivityTimerStart(self, "printTimeCountdown", 1) end end function SetPlayerSpawnPoints(self, playerID) local iTeamID = 0 local iOffsetID = 0 local bFound = false for teamIndex, team in ipairs(teamTable) do for offsetIndex, pID in ipairs(team) do if(playerID:GetID() == pID:GetID()) then iTeamID = teamIndex iOffsetID = offsetIndex bFound = true break end end if(bFound) then break end end local pointGroup = "Spawn" .. tostring(iTeamID) .. "_" .. tostring(iOffsetID) -- print("@@@@@ " .. playerID:GetName().name .. " - SPAWNING AT: " .. pointGroup) local spawnObjs = self:GetObjectsInGroup{group = pointGroup, ignoreSpawners = true}.objects for i, spawnObject in ipairs(spawnObjs) do if(spawnObject:Exists()) then local pos = spawnObject:GetPosition().pos local rot = spawnObject:GetRotation() playerID:Teleport{pos = pos, x = rot.x, y = rot.y, z = rot.z, w = rot.w, bSetRotation = true} break end end end ---------------------------------------------------------------- -- Received a fire event messaged from the client ---------------------------------------------------------------- function onFireEventServerSide(self, msg) if msg.args == "CheckPlayerCount" then ProcessPlayerCount(self) end end function onFireEvent(self, msg) -- Receive the sending object ID and the message to parse local eventType = msg.args local sendObj = msg.senderID:GetID() -- Missing a valid event type? if not eventType then return end -- Obtain the current super shot table local superShotTable = self:GetVar("superShotTable") or {} if eventType == "incSuperShotTbl" then -- If there is a valid player, increment accordingly -- otherwise establish a new player entry if(superShotTable[sendObj]) then -- We have an entry for this player, update it superShotTable[sendObj] = superShotTable[sendObj] + 1 -- Clamp to five if(superShotTable[sendObj] > 5) then superShotTable[sendObj] = 5 end else superShotTable[sendObj] = 1 end elseif eventType == "resetSuperShotTbl" then if(superShotTable[sendObj]) then -- Reset the super shot counter superShotTable[sendObj] = 0 end end -- print("@@@@@ SUPER SHOT GUAGE = " .. tostring(superShotTable[sendObj])) -- Save the table update self:SetVar("superShotTable", superShotTable) end ---------------------------------------------------------------- -- A player has been resurrected ---------------------------------------------------------------- function basePlayerResurrected(self, msg, newMsg) ProcessPlayerCount(self) end ---------------------------------------------------------------- -- A player has respawned ---------------------------------------------------------------- function onPlayerResurrected(self, msg) basePlayerResurrected(self, msg, newMsg) end ---------------------------------------------------------------- -- This is called when players hit the UI to exit or stop the game. ---------------------------------------------------------------- function onMessageBoxRespond(self,msg) baseMessageBoxRespond(self, msg, newMsg) end