Menu
CoddyTech

Moedas do Deserto

Lição 16 de 18 do curso Jogo Roblox: Simulador de Pets da Coddy.

O Deserto tem seus próprios pontos, e suas pilhas são mais resistentes e valiosas: Settings.DesertHealth e Settings.DesertValue. Gere pilhas em cada zona e dê a cada uma um atributo Zone indicando onde ela fica. Um ponto conhece sua zona: ele fica em Zones.Desert.Spawns, então spot.Parent.Parent.Name é "Desert".

O portão só abre na tela do jogador, e um explorador pode abrir qualquer parede na tela dele. Por isso, a verificação Hit do servidor ganha mais uma regra: uma pilha do Deserto ignora jogadores sem o atributo Desert. O cliente abre portas; o servidor mantém as regras.

challenge icon

Desafio

Difícil
  1. Em spawnPile, use a vida e o valor do Desert para os pontos no Desert e defina o atributo Zone de cada pilha com o nome da zona correspondente.
  2. Gere pilhas nos pontos de todas as zonas em workspace.Zones.
  3. No manipulador Hit, ignore uma pilha do Desert para um jogador cujo atributo Desert não esteja definido.

Pressione Play e depois Check.

Experimente você mesmo

local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
local RunService = game:GetService("RunService")
local Settings = require(game.ReplicatedStorage.Settings)

local remotes = game.ReplicatedStorage.Remotes
local piles = workspace.Piles
local petParts = workspace.PetParts
local pileSpot = {} -- cada pilha -> o local onde ela está
local lastHit = {} -- cada jogador -> quando deu o último golpe

print("A Meadow pile gives " .. Settings.PileValue .. " coins")

-- Settings.Pets por nome
local petInfo = {}
for _, info in Settings.Pets do
	petInfo[info.Name] = info
end

-- Um pet: um StringValue na pasta Pets do jogador que armazena seu nome, com
-- sua raridade e seu poder como atributos
local function givePet(player, name, equipped)
	local info = petInfo[name]
	local pet = Instance.new("StringValue")
	pet.Name = "Pet"
	pet.Value = name
	pet:SetAttribute("Rarity", info.Rarity)
	pet:SetAttribute("Power", info.Power)
	pet:SetAttribute("Equipped", equipped)
	pet.Parent = player.Pets
	return pet
end

-- quantos pets um jogador tem em campo
local function equippedCount(player)
	local count = 0
	for _, pet in player.Pets:GetChildren() do
		if pet:GetAttribute("Equipped") then
			count += 1
		end
	end
	return count
end

-- quanto os pets de um jogador acrescentam a cada golpe
local function petPower(player)
	local power = 0
	for _, pet in player.Pets:GetChildren() do
		if pet:GetAttribute("Equipped") then
			power += pet:GetAttribute("Power")
		end
	end
	return power
end

-- Todo jogador recebe Coins no placar
Players.PlayerAdded:Connect(function(player)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player

	local coins = Instance.new("IntValue")
	coins.Name = "Coins"
	coins.Parent = leaderstats

	-- os pets que possuem
	local pets = Instance.new("Folder")
	pets.Name = "Pets"
	pets.Parent = player
end)

-- Uma pilha de moedas em um local: uma cópia de ServerStorage.CoinPile com sua vida
-- e seu valor como atributos
local function spawnPile(spot)
	local pile = ServerStorage.CoinPile:Clone()
	pile.Position = spot.Position
	pile:SetAttribute("MaxHealth", Settings.PileHealth)
	pile:SetAttribute("Health", Settings.PileHealth)
	pile:SetAttribute("Value", Settings.PileValue)
	pile.HealthGui.Label.Text = pile:GetAttribute("Health") .. "/" .. pile:GetAttribute("MaxHealth")
	pileSpot[pile] = spot
	pile.Parent = piles
	return pile
end

-- Um golpe reduz a vida de uma pilha em 1, mais o poder dos pets do jogador;
-- ao chegar a 0, a pilha se desfaz e concede seu valor
local function hitPile(player, pile)
	local health = pile:GetAttribute("Health") - (1 + petPower(player))
	if health > 0 then
		pile:SetAttribute("Health", health)
		pile.HealthGui.Label.Text = health .. "/" .. pile:GetAttribute("MaxHealth")
		return
	end
	player.leaderstats.Coins.Value += pile:GetAttribute("Value")
	local spot = pileSpot[pile]
	pileSpot[pile] = nil
	pile:Destroy()
	-- uma nova pilha no mesmo lugar, alguns segundos depois
	task.delay(Settings.RespawnTime, spawnPile, spot)
end

-- Uma pilha em cada ponto de Meadow
for _, spot in workspace.Zones.Meadow.Spawns:GetChildren() do
	spawnPile(spot)
end

-- O cliente solicita um golpe em uma pilha. Nunca confie no cliente: qualquer pessoa pode acionar
-- esse remote com qualquer coisa, de qualquer lugar, tão rápido quanto quiser.
remotes.Hit.OnServerEvent:Connect(function(player, pile)
	-- uma pilha de verdade
	if typeof(pile) ~= "Instance" or pile.Parent ~= piles then
		return
	end
	-- ao alcance
	local character = player.Character
	local root = character and character:FindFirstChild("HumanoidRootPart")
	if not root or (root.Position - pile.Position).Magnitude > Settings.Reach then
		return
	end
	-- sem agir rápido demais
	local now = time()
	if lastHit[player] and now - lastHit[player] < Settings.HitCooldown then
		return
	end
	lastHit[player] = now
	hitPile(player, pile)
end)

-- Um sorteio ponderado: a Chance de cada pet (de um total de 100) é sua parcela do intervalo
local function rollPet()
	local roll = math.random() * 100
	for _, info in Settings.Pets do
		roll -= info.Chance
		if roll <= 0 then
			return info.Name
		end
	end
	return Settings.Pets[1].Name
end

-- Chocagem: o prompt do ovo, pago com moedas
workspace.Egg.ProximityPrompt.Triggered:Connect(function(player)
	local coins = player.leaderstats.Coins
	if coins.Value < Settings.EggPrice then
		return
	end
	coins.Value -= Settings.EggPrice
	local name = rollPet()
	-- já sai equipado, se houver espaço
	local pet = givePet(player, name, equippedCount(player) < Settings.MaxEquipped)
	print(player.Name .. " hatched a " .. name)
end)

-- Os pets equipados seguem seu dono: cada um tem uma peça no mundo, mantida em uma
-- em fila atrás do jogador a cada quadro
local petModels = {} -- cada pet -> sua peça no mundo
RunService.Heartbeat:Connect(function()
	for _, player in Players:GetPlayers() do
		local root = player.Character and player.Character:FindFirstChild("HumanoidRootPart")
		local pets = player:FindFirstChild("Pets")
		local slot = 0
		for _, pet in pets and pets:GetChildren() or {} do
			local model = petModels[pet]
			if pet:GetAttribute("Equipped") and root then
				slot += 1
				if not model then
					model = ServerStorage.PetModels[pet.Value]:Clone()
					model.Parent = petParts
					petModels[pet] = model
				end
				model.CFrame = root.CFrame * CFrame.new((slot - 2) * 3, -1.5, 4)
			elseif model then
				model:Destroy()
				petModels[pet] = nil
			end
		end
	end
end)

-- O cliente pede para colocar um pet em campo ou guardá-lo. O servidor verifica se o pet é
-- do próprio jogador e se não há mais de MaxEquipped pets em campo.
remotes.Equip.OnServerEvent:Connect(function(player, pet)
	if typeof(pet) ~= "Instance" or pet.Parent ~= player.Pets then
		return
	end
	if pet:GetAttribute("Equipped") then
		pet:SetAttribute("Equipped", false)
	elseif equippedCount(player) < Settings.MaxEquipped then
		pet:SetAttribute("Equipped", true)
	end
end)

-- O portão do Desert: pague uma vez e ele se abre apenas para esse jogador
workspace.Gate.ProximityPrompt.Triggered:Connect(function(player)
	local coins = player.leaderstats.Coins
	if player:GetAttribute("Desert") or coins.Value < Settings.GatePrice then
		return
	end
	coins.Value -= Settings.GatePrice
	player:SetAttribute("Desert", true)
end)
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Jogo Roblox: Simulador de Pets