Menu
CoddyTech

Đồng xu sa mạc

Bài học 17 trên 20 trong khóa học Trò chơi Roblox: mô phỏng thú cưng của Coddy.

Sa mạc có các điểm xuất hiện riêng, cùng những đống tài nguyên cứng hơn và có giá trị cao hơn: Settings.DesertHealth và Settings.DesertValue. Tạo các đống tài nguyên ở mọi khu vực và thêm thuộc tính Zone cho mỗi đống để cho biết nó thuộc khu vực nào. Mỗi điểm xuất hiện biết khu vực của mình: nó nằm trong Zones.Desert.Spawns, nên spot.Parent.Parent.Name là "Desert".

Cổng chỉ mở trên màn hình của người chơi, và kẻ khai thác có thể mở bất kỳ bức tường nào trên màn hình của họ. Vì vậy, quy tắc kiểm tra Hit của máy chủ có thêm một điều kiện: đống tài nguyên ở Sa mạc sẽ bỏ qua những người chơi không có thuộc tính Desert. Máy khách mở cửa, máy chủ duy trì các quy tắc.

challenge icon

Thử thách

Khó

Lấp đầy sa mạc bằng những đống xu cứng cáp hơn và giá trị hơn.

  1. Trong spawnPile, với một vị trí có spot.Parent.Parent.Name là Desert, hãy dùng Settings.DesertHealth và Settings.DesertValue; đặt thuộc tính Zone của mỗi đống xu thành tên đó.
  2. Tạo đống xu tại các vị trí của mọi vùng trong workspace.Zones.
  3. Trong trình xử lý Hit, bỏ qua đống xu ở sa mạc nếu thuộc tính Desert của người chơi chưa được đặt.

Nhấn Chơi: các đống xu có nhãn 8/8 nằm trên cát. Sau đó nhấn Kiểm tra.

Tự làm thử

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 = {} -- mỗi đống xu -> vị trí đặt đống xu đó
local lastHit = {} -- mỗi người chơi -> thời điểm họ đánh lần cuối

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

-- Settings.Pets theo tên
local petInfo = {}
for _, info in Settings.Pets do
	petInfo[info.Name] = info
end

-- Thú cưng: một StringValue trong thư mục Pets của người chơi, lưu tên cùng
-- độ hiếm và sức mạnh dưới dạng thuộc tính
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

-- số thú cưng người chơi đang cho ra ngoài
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

-- sức mạnh thú cưng cộng thêm vào mỗi lần người chơi đánh
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

-- Mỗi người chơi có Coins trên bảng xếp hạng
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

	-- những thú cưng họ sở hữu
	local pets = Instance.new("Folder")
	pets.Name = "Pets"
	pets.Parent = player
end)

-- Đống xu tại một vị trí: bản sao của ServerStorage.CoinPile, có máu
-- và giá trị dưới dạng thuộc tính
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

-- Mỗi lần đánh trừ 1 máu của đống xu, cộng thêm sức mạnh từ thú cưng của người chơi;
-- Khi còn 0 máu, đống xu vỡ và trả số xu theo giá trị của nó
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()
	-- Vài giây sau, một đống xu mới xuất hiện ở cùng vị trí
	task.delay(Settings.RespawnTime, spawnPile, spot)
end

-- Bài học 17: Desert Coins sẽ nằm ở đây

-- Mỗi vị trí trong Meadow có một đống xu
for _, spot in workspace.Zones.Meadow.Spawns:GetChildren() do
	spawnPile(spot)
end

-- Client yêu cầu đánh đống xu. Đừng tin client: bất kỳ ai cũng có thể gửi
-- remote này với bất kỳ dữ liệu nào, từ bất kỳ đâu, nhanh tùy ý.
remotes.Hit.OnServerEvent:Connect(function(player, pile)
	-- đống xu phải tồn tại
	if typeof(pile) ~= "Instance" or pile.Parent ~= piles then
		return
	end
	-- trong tầm đánh
	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
	-- không đánh quá nhanh
	local now = time()
	if lastHit[player] and now - lastHit[player] < Settings.HitCooldown then
		return
	end
	lastHit[player] = now
	hitPile(player, pile)
end)

-- Chọn ngẫu nhiên có trọng số: Chance của mỗi thú cưng, tính trên 100, là phần của nó trên dải xác suất
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

-- Ấp trứng: dùng lời nhắc tương tác của quả trứng và trả bằng xu
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()
	-- cho thú cưng ra ngay nếu còn chỗ
	local pet = givePet(player, name, equippedCount(player) < Settings.MaxEquipped)
	print(player.Name .. " hatched a " .. name)
end)

-- Thú cưng đang được trang bị đi theo chủ: mỗi con có một bộ phận trong thế giới,
-- xếp thành hàng sau người chơi ở mỗi khung hình
local petModels = {} -- mỗi thú cưng -> bộ phận tương ứng trong thế giới
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
				-- cất đi: bộ phận tương ứng biến mất
				model:Destroy()
				petModels[pet] = nil
			end
		end
	end
end)

-- Client yêu cầu trang bị hoặc cất thú cưng. Máy chủ kiểm tra: thú cưng
-- thuộc về người chơi, và số thú cưng được trang bị không vượt quá MaxEquipped.
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)

-- Cổng Desert: trả phí một lần để mở cổng, chỉ cho người chơi đó
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)

Tất cả bài học trong Trò chơi Roblox: mô phỏng thú cưng

Tự luyện tập: Playground Roblox