Almost finished garage!
This commit is contained in:
parent
3ff5337437
commit
05c424e6c1
2
.vscode/settings.json
vendored
Normal file
2
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
{
|
||||
}
|
||||
@ -34,6 +34,7 @@
|
||||
"ServerScriptService": {
|
||||
"$path": "src/server"
|
||||
},
|
||||
|
||||
"StarterPlayer": {
|
||||
"StarterPlayerScripts": {
|
||||
"$path": "src/client"
|
||||
|
||||
4
luau-lsp.json
Normal file
4
luau-lsp.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"luau-lsp.sourcemap.enabled": true,
|
||||
"luau-lsp.sourcemap.file": "sourcemap.json"
|
||||
}
|
||||
@ -2,6 +2,8 @@ local DataManager = {}
|
||||
|
||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||
|
||||
local Template = require(script.Parent.Template)
|
||||
local DataTypes = require(ReplicatedStorage.Data.DataTypes)
|
||||
local OrderedDataStore = require("./OrderedDatastoreHandler")
|
||||
|
||||
local ValueNames = require("./ValueNames")
|
||||
@ -9,26 +11,24 @@ local DataStoreNames = require("./DataStoreNames")
|
||||
|
||||
local ProfileStore
|
||||
|
||||
|
||||
|
||||
-- Store profiles from ProfileStore
|
||||
DataManager.Profiles = {}
|
||||
|
||||
local synced = {}
|
||||
|
||||
function DataManager.getKey(player : Player,key : string)
|
||||
function DataManager.getKey(player: Player, key: string)
|
||||
return "PLAYER_" .. player.UserId .. "_" .. key
|
||||
end
|
||||
function DataManager.AddValue(player : Player,key : string,num)
|
||||
local value = DataManager.GetValue(player,key)
|
||||
function DataManager.AddValue(player: Player, key: string, num)
|
||||
local value = DataManager.GetValue(player, key)
|
||||
if not value then
|
||||
return
|
||||
end
|
||||
|
||||
DataManager.SetValue(player, key, value + num)
|
||||
end
|
||||
function DataManager.SubValue(player : Player,key : number,num)
|
||||
local value = DataManager.GetValue(player,key)
|
||||
function DataManager.SubValue(player: Player, key: number, num)
|
||||
local value = DataManager.GetValue(player, key)
|
||||
if not value then
|
||||
return
|
||||
end
|
||||
@ -36,7 +36,7 @@ function DataManager.SubValue(player : Player,key : number,num)
|
||||
DataManager.SetValue(player, key, value - num)
|
||||
end
|
||||
|
||||
function DataManager.GetValue(player : Player,key : string)
|
||||
function DataManager.GetValue(player: Player, key: string)
|
||||
local profile = DataManager.Profiles[player]
|
||||
if not profile then
|
||||
return
|
||||
@ -44,7 +44,7 @@ function DataManager.GetValue(player : Player,key : string)
|
||||
return profile.Data[key]
|
||||
end
|
||||
|
||||
function DataManager.SetValue(player : Player,key : string,num)
|
||||
function DataManager.SetValue(player: Player, key: string, num)
|
||||
local profile = DataManager.Profiles[player]
|
||||
if not profile then
|
||||
return
|
||||
@ -52,7 +52,7 @@ function DataManager.SetValue(player : Player,key : string,num)
|
||||
|
||||
profile.Data[key] = num
|
||||
|
||||
local keyName = DataManager.getKey(player,key)
|
||||
local keyName = DataManager.getKey(player, key)
|
||||
local s = synced[keyName]
|
||||
if not s then
|
||||
return
|
||||
@ -64,43 +64,47 @@ function DataManager.SetValue(player : Player,key : string,num)
|
||||
end
|
||||
local datastore = s.Datastore
|
||||
if datastore then
|
||||
OrderedDataStore.SaveData(player,datastore,num)
|
||||
OrderedDataStore.SaveData(player, datastore, num)
|
||||
end
|
||||
end
|
||||
|
||||
function DataManager.GetTanks(player : Player)
|
||||
function DataManager.CreateLoadout(player, tank, loadoutData: DataTypes.Loadout)
|
||||
local profile = DataManager.GetPlayerData(player)
|
||||
|
||||
local tankLoadoutsTbl = profile.Data[tank]
|
||||
|
||||
if not tankLoadoutsTbl then
|
||||
profile.Data[tank] = {}
|
||||
end
|
||||
end
|
||||
|
||||
function DataManager.GetTankData(player : Player,name : string)
|
||||
function DataManager.RemoveLoadout(player) end
|
||||
|
||||
function DataManager.UpdateLoadout() end
|
||||
|
||||
function DataManager.GetPlayerData(player: Player): typeof(Template)
|
||||
return DataManager.Profiles[player]
|
||||
end
|
||||
|
||||
function DataManager.SwitchTank(player : Player,tank : string)
|
||||
function DataManager.GetTanks(player: Player) end
|
||||
|
||||
end
|
||||
function DataManager.GetTankData(player: Player, name: string) end
|
||||
|
||||
function DataManager.SwitchTankSkin(player : Player,skinName : string)
|
||||
function DataManager.SwitchTank(player: Player, tank: string) end
|
||||
|
||||
end
|
||||
|
||||
function DataManager.UnlockTank(player : Player,tank : string)
|
||||
|
||||
end
|
||||
|
||||
function DataManager.UnlockTankSkin(player : Player,skinName)
|
||||
|
||||
end
|
||||
function DataManager.SwitchTankSkin(player: Player, skinName: string) end
|
||||
|
||||
function DataManager.UnlockTank(player: Player, tank: string) end
|
||||
|
||||
function DataManager.UnlockTankSkin(player: Player, skinName) end
|
||||
|
||||
-- yh ill add it later
|
||||
|
||||
function DataManager.SyncValue(player : Player,value : IntValue,key : string,datastore : string)
|
||||
local keyName = DataManager.getKey(player,key)
|
||||
synced[keyName] = {Value = value,Datastore = datastore}
|
||||
function DataManager.SyncValue(player: Player, value: IntValue, key: string, datastore: string)
|
||||
local keyName = DataManager.getKey(player, key)
|
||||
synced[keyName] = { Value = value, Datastore = datastore }
|
||||
|
||||
DataManager.SetValue(player,key,DataManager.GetValue(player,key))
|
||||
DataManager.SetValue(player, key, DataManager.GetValue(player, key))
|
||||
end
|
||||
|
||||
--sup
|
||||
|
||||
@ -1,135 +1,173 @@
|
||||
local HttpService = game:GetService("HttpService")
|
||||
local Players = game:GetService("Players")
|
||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||
local ServerScriptService = game:GetService("ServerScriptService")
|
||||
local RemoteFunction = ReplicatedStorage.Remote.InventoryService
|
||||
|
||||
local Template = require(script.Parent.Template)
|
||||
local DataTypes = require(ReplicatedStorage.Data.DataTypes)
|
||||
local DataManager = require(ServerScriptService.Data.DataManager)
|
||||
|
||||
local Remote = ReplicatedStorage.Remote
|
||||
local rfn_Inventory = Remote:WaitForChild("InventoryService") :: RemoteFunction
|
||||
|
||||
local InventoryService = {}
|
||||
|
||||
local PlayersInventory : {[number] : {}} = {}
|
||||
-- ─────────────────────────────────────────
|
||||
-- Internal
|
||||
-- ─────────────────────────────────────────
|
||||
|
||||
local Tanks: {string} = {}
|
||||
for _, tank in ReplicatedStorage:WaitForChild("Assets"):WaitForChild("Models"):WaitForChild("Tanks"):GetChildren() do
|
||||
if tank.Name ~= "Tank (Testing)" or game:GetService("RunService"):IsStudio() then
|
||||
table.insert(Tanks, tank.Name)
|
||||
end
|
||||
end
|
||||
local Skins: {[typeof(Tanks)] : string} = {}
|
||||
for _, tank in Tanks do
|
||||
Skins[tank] = {}
|
||||
for _, skin in ReplicatedStorage:WaitForChild("Assets"):WaitForChild("Models"):WaitForChild("Tanks"):WaitForChild(tank):GetChildren() do
|
||||
if skin.Name ~= "Base" then
|
||||
table.insert(Skins[tank], skin.Name)
|
||||
end
|
||||
type Inventory = typeof(Template.Inventory)
|
||||
|
||||
local function getInventory(player: Player): Inventory?
|
||||
local profile = DataManager.Profiles[player]
|
||||
if not profile then
|
||||
return nil
|
||||
end
|
||||
print(profile.Data.Inventory)
|
||||
return profile.Data.Inventory
|
||||
end
|
||||
|
||||
function InventoryService.GetModel(tankName : string, skinName : string)
|
||||
local base = ReplicatedStorage:WaitForChild("Assets"):WaitForChild("Models"):WaitForChild("Tanks"):WaitForChild(tankName):FindFirstChild("Base")
|
||||
local skin = ReplicatedStorage:WaitForChild("Assets"):WaitForChild("Models"):WaitForChild("Tanks"):WaitForChild(tankName):FindFirstChild(skinName)
|
||||
if base and skin then
|
||||
return base, skin
|
||||
local function syncToClient(player: Player)
|
||||
if true then
|
||||
return
|
||||
end
|
||||
local inv = getInventory(player)
|
||||
--if inv then
|
||||
-- rev_InventorySync:FireClient(player, inv)
|
||||
--end
|
||||
end
|
||||
|
||||
function createPlayerInventory(player : Player)
|
||||
print("Creating inventory: ".. player.Name)
|
||||
PlayersInventory[player.UserId] = {}
|
||||
local Loadouts: {[string] : string} = {}
|
||||
local SelectedLoadout: string
|
||||
local PlayerSkins: {[string] : string} = {}
|
||||
|
||||
local Inventory = {}
|
||||
Inventory["SelectedLoadout"] = ""
|
||||
Inventory["Loadouts"] = Loadouts
|
||||
Inventory["Tanks"] = {}
|
||||
Inventory["Skins"] = PlayerSkins
|
||||
PlayersInventory[player.UserId] = Inventory
|
||||
|
||||
if Inventory["Tanks"] == nil or #Inventory["Tanks"] == 0 then
|
||||
Inventory["Tanks"] = {"Tank"}
|
||||
end
|
||||
|
||||
if Inventory["SelectedLoadout"] == "" or Inventory["SelectedLoadout"] == nil or table.find(Inventory["Tanks"], Inventory["SelectedLoadout"]) then
|
||||
print(InventoryService.SelectLoadout(player, Inventory["Tanks"][1]))
|
||||
end
|
||||
|
||||
for _, tank in Inventory["Tanks"] do
|
||||
if Inventory["Skins"][tank] == nil or #Inventory["Skins"][tank] == 0 then
|
||||
Inventory["Skins"][tank] = {"Default"}
|
||||
end
|
||||
end
|
||||
|
||||
if not CheckIfLoadoutExists(player, Inventory["Tanks"][1]) then
|
||||
InventoryService.UpdateLoadout(player, Inventory["SelectedLoadout"], Inventory["Skins"][Inventory["Tanks"][1]][1])
|
||||
end
|
||||
|
||||
return Inventory
|
||||
function InventoryService.GetInventory(player: Player): Inventory?
|
||||
return getInventory(player)
|
||||
end
|
||||
|
||||
function InventoryService.GetAllTanks()
|
||||
return Tanks
|
||||
-- Returns the skin currently equipped for a given tank.
|
||||
function InventoryService.GetSelectedSkin(player: Player, tankName: string): string
|
||||
local inv = getInventory(player)
|
||||
if not inv then
|
||||
return "Default"
|
||||
end
|
||||
return inv.Loadouts[tankName] or "Default"
|
||||
end
|
||||
|
||||
function InventoryService.GetAllSkins()
|
||||
return Skins
|
||||
function InventoryService.GetCurrentLoadout(player: Player)
|
||||
local inv = getInventory(player)
|
||||
local current = inv.CurrentLoadouts[inv.SelectedTank]
|
||||
return current
|
||||
end
|
||||
|
||||
function InventoryService.CreateLoadout(player, tankName, loadout: DataTypes.Loadout): string
|
||||
local inv = getInventory(player)
|
||||
local randomId = HttpService:GenerateGUID(false)
|
||||
inv.Loadouts[tankName][randomId] = loadout
|
||||
|
||||
function InventoryService.UpdateLoadout(player : Player, tank : string, skin : string)
|
||||
PlayersInventory[player.UserId]["Loadouts"][tank] = skin
|
||||
return PlayersInventory[player.UserId]["Loadouts"][tank]
|
||||
return randomId
|
||||
end
|
||||
|
||||
function InventoryService.SelectLoadout(player : Player, loadout : string)
|
||||
PlayersInventory[player.UserId]["SelectedLoadout"] = loadout
|
||||
return PlayersInventory[player.UserId]["SelectedLoadout"]
|
||||
end
|
||||
function InventoryService.SelectTank(player: Player, tankName: string): (boolean, string?)
|
||||
local inv = getInventory(player)
|
||||
if not inv then
|
||||
return false, "Profile not loaded"
|
||||
end
|
||||
if not table.find(inv.Tanks, tankName) then
|
||||
return false, "Tank not owned"
|
||||
end
|
||||
|
||||
function CheckIfLoadoutExists(player : Player, tank : string)
|
||||
local Loadouts = PlayersInventory[player.UserId].Loadouts
|
||||
if Loadouts[tank] then
|
||||
inv.SelectedTank = tankName
|
||||
syncToClient(player)
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
-- Sets which skin is equipped for a specific tank.
|
||||
function InventoryService.UpdateLoadout(player: Player, tank: string, skin: string): (boolean, string?)
|
||||
local inv = getInventory(player)
|
||||
if not inv then
|
||||
return false, "Profile not loaded"
|
||||
end
|
||||
end
|
||||
|
||||
function InventoryService.GetInventory(player : Player)
|
||||
return PlayersInventory[player.UserId]
|
||||
end
|
||||
|
||||
function InventoryService.AllInventories()
|
||||
return PlayersInventory
|
||||
end
|
||||
|
||||
function InventoryService.Init()
|
||||
for _, player in Players:GetPlayers() do
|
||||
createPlayerInventory(player)
|
||||
if not table.find(inv.Tanks, tank) then
|
||||
return false, "Tank not owned"
|
||||
end
|
||||
Players.PlayerAdded:Connect(function(player)
|
||||
createPlayerInventory(player)
|
||||
end)
|
||||
Players.PlayerRemoving:Connect(function(player)
|
||||
PlayersInventory[player.UserId] = nil
|
||||
end)
|
||||
RemoteFunction.OnServerInvoke = function(player, action, ...)
|
||||
local args = {...}
|
||||
|
||||
local ownedSkins = inv.Skins[tank]
|
||||
if not ownedSkins or not table.find(ownedSkins, skin) then
|
||||
return false, "Skin not owned"
|
||||
end
|
||||
|
||||
inv.Loadouts[tank] = skin
|
||||
syncToClient(player)
|
||||
return true
|
||||
end
|
||||
|
||||
function InventoryService.UnlockTank(player: Player, tankName: string)
|
||||
local inv = getInventory(player)
|
||||
if not inv then
|
||||
return
|
||||
end
|
||||
if table.find(inv.Tanks, tankName) then
|
||||
return
|
||||
end -- already owned
|
||||
|
||||
table.insert(inv.Tanks, tankName)
|
||||
InventoryService.UnlockSkin(player, tankName, "Default")
|
||||
|
||||
local loadout: DataTypes.Loadout = { Name = "Default", Tank = tankName }
|
||||
InventoryService.CreateLoadout(player, tankName, loadout)
|
||||
inv.CurrentLoadouts[tankName] = loadout
|
||||
|
||||
syncToClient(player)
|
||||
end
|
||||
|
||||
function InventoryService.UnlockSkin(player: Player, tankName: string, skinName: string)
|
||||
local inv = getInventory(player)
|
||||
if not inv then
|
||||
return
|
||||
end
|
||||
if not table.find(inv.Tanks, tankName) then
|
||||
return
|
||||
end
|
||||
|
||||
inv.Skins[tankName] = inv.Skins[tankName] or {}
|
||||
if table.find(inv.Skins[tankName], skinName) then
|
||||
return
|
||||
end -- already owned
|
||||
|
||||
table.insert(inv.Skins[tankName], skinName)
|
||||
syncToClient(player)
|
||||
end
|
||||
|
||||
-- ─────────────────────────────────────────
|
||||
-- Init (called by ModuleLoader)
|
||||
-- ─────────────────────────────────────────
|
||||
|
||||
function InventoryService:Init()
|
||||
-- Handle all client requests through one RemoteFunction.
|
||||
-- Each action is validated server-side — client input is never trusted.
|
||||
rfn_Inventory.OnServerInvoke = function(player: Player, action: string, ...)
|
||||
if action == "GetInventory" then
|
||||
return InventoryService.GetInventory(player)
|
||||
elseif action == "AllInventories" then
|
||||
return InventoryService.AllInventories()
|
||||
elseif action == "SelectTank" then
|
||||
local ok, err = InventoryService.SelectTank(player, ...)
|
||||
return ok, err
|
||||
elseif action == "UpdateLoadout" then
|
||||
InventoryService.UpdateLoadout(player, args[1], args[2])
|
||||
elseif action == "SelectLoadout" then
|
||||
return InventoryService.SelectLoadout(player, args[1])
|
||||
elseif action == "GetAllTanks" then
|
||||
return InventoryService.GetAllTanks()
|
||||
elseif action == "GetAllSkins" then
|
||||
return InventoryService.GetAllSkins()
|
||||
elseif action == "GetModel" then
|
||||
return InventoryService.GetModel(args[1], args[2])
|
||||
local ok, err = InventoryService.UpdateLoadout(player, ...)
|
||||
return ok, err
|
||||
end
|
||||
|
||||
return false, "Unknown action: " .. tostring(action)
|
||||
end
|
||||
|
||||
-- Sync inventory to client once their profile is ready.
|
||||
-- DataManager loads profiles on PlayerAdded, so we wait for it.
|
||||
Players.PlayerAdded:Connect(function(player)
|
||||
local waited = 0
|
||||
while not DataManager.Profiles[player] and waited < 10 do
|
||||
task.wait(0.5)
|
||||
waited += 0.5
|
||||
end
|
||||
|
||||
if DataManager.Profiles[player] then
|
||||
syncToClient(player)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
return InventoryService
|
||||
|
||||
@ -1,12 +1,19 @@
|
||||
--!nonstrict
|
||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||
local DataTypes = require(ReplicatedStorage.Data.DataTypes)
|
||||
|
||||
local Inventory: DataTypes.Inventory = {
|
||||
Tanks = { "Tank" }, -- tanks the player owns
|
||||
Skins = { Tank = { "Default" } }, -- skins per tank
|
||||
Loadouts = {} :: { [string]: { [string]: DataTypes.Loadout } }, -- Saved loadouts,
|
||||
CurrentLoadouts = {} :: { [string]: DataTypes.Loadout }, -- Current loadout of tank
|
||||
|
||||
SelectedTank = "Tank",
|
||||
}
|
||||
|
||||
return {
|
||||
Money = 50,
|
||||
Wins = 0,
|
||||
|
||||
UnlockedSkins = {},
|
||||
UnlockedTanks = {},
|
||||
UnlockedAccessories = {},
|
||||
|
||||
Loadouts = {},
|
||||
CurrentLoadout = "Default",
|
||||
CurrentTank = "Tank"
|
||||
Inventory = Inventory,
|
||||
}
|
||||
@ -3,11 +3,11 @@
|
||||
local GameObject = require(script.Parent)
|
||||
local InventoryService = require(game:GetService("ServerScriptService").Data.InventoryService)
|
||||
|
||||
local Bot = setmetatable({},GameObject)
|
||||
local Bot = setmetatable({}, GameObject)
|
||||
Bot.__index = Bot
|
||||
|
||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||
|
||||
local StarterGui = game:GetService("StarterGui")
|
||||
|
||||
local rAssets = ReplicatedStorage.Assets
|
||||
local rHUD = rAssets.HUD
|
||||
@ -18,28 +18,27 @@ local TankFolder = rModels.Tanks
|
||||
local rData = ReplicatedStorage.Data
|
||||
local BotData = require(rData.BotData)
|
||||
|
||||
|
||||
type self = {
|
||||
accuracy : number,
|
||||
HUD : typeof(rHUD.HUD_TANK)
|
||||
accuracy: number,
|
||||
HUD: typeof(rHUD.HUD_TANK),
|
||||
}
|
||||
|
||||
export type Bot = typeof( setmetatable({} :: self, Bot) ) & GameObject.GameObject
|
||||
export type Bot = typeof(setmetatable({} :: self, Bot)) & GameObject.GameObject
|
||||
|
||||
local Component = script.Parent.Parent.Component
|
||||
local HitboxComponent = require(Component.HitboxComponent)
|
||||
local ProjectileComponent = require(Component.ProjectileComponent)
|
||||
local HealthComponent = require(Component.HealthComponent)
|
||||
|
||||
function Bot.new(player : Player, name, character)
|
||||
local self : Bot = setmetatable(GameObject.new(player.UserId,nil,player.UserId) ,Bot)
|
||||
function Bot.new(player: Player, name, character)
|
||||
local self: Bot = setmetatable(GameObject.new(player.UserId, nil, player.UserId), Bot)
|
||||
local Inventory = InventoryService.GetInventory(player)
|
||||
local selectedTank = Inventory["SelectedTank"]
|
||||
local selectedSkin = Inventory["Loadouts"][selectedTank]
|
||||
name = selectedTank or "Tank"
|
||||
local skin = selectedSkin or "Default"
|
||||
local bData : BotData.BotData = BotData[name]
|
||||
print(name,bData)
|
||||
local bData: BotData.BotData = BotData[name]
|
||||
print(name, bData)
|
||||
|
||||
print(name)
|
||||
self.name = name
|
||||
@ -49,10 +48,9 @@ function Bot.new(player : Player, name, character)
|
||||
|
||||
self:CreateModel()
|
||||
|
||||
local h = HealthComponent.new(self.key,bData.maxHp)
|
||||
local h = HealthComponent.new(self.key, bData.maxHp)
|
||||
|
||||
|
||||
h.HealthChangedSignal:Connect(function(oldHP,newHP)
|
||||
h.HealthChangedSignal:Connect(function(oldHP, newHP)
|
||||
local diff = oldHP - newHP
|
||||
if oldHP > newHP then
|
||||
print(self.name .. " took " .. diff .. " damage!")
|
||||
@ -68,47 +66,40 @@ function Bot.new(player : Player, name, character)
|
||||
|
||||
self.Components.Health = h
|
||||
|
||||
self.Components.Hitbox = HitboxComponent.new(self.key,self.model)
|
||||
self.Components.Hitbox = HitboxComponent.new(self.key, self.model)
|
||||
|
||||
self.Components.Health:Init()
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
function Bot:CreateModel()
|
||||
print(self.name)
|
||||
print(TankFolder)
|
||||
local model : Model = TankFolder:FindFirstChild(self.name):FindFirstChild("Base")
|
||||
local model: Model = TankFolder:FindFirstChild(self.name):FindFirstChild("Base")
|
||||
model = model:Clone()
|
||||
local skin : Model = TankFolder:FindFirstChild(self.name):FindFirstChild(self.skin)
|
||||
local skin: Model = TankFolder:FindFirstChild(self.name):FindFirstChild(self.skin)
|
||||
skin.Parent = model
|
||||
|
||||
print(self.key)
|
||||
model.Name = self.key
|
||||
model:SetAttribute("Type",self.name)
|
||||
model:SetAttribute("Type", self.name)
|
||||
self.model = model
|
||||
self.root = model.PrimaryPart or model.Root
|
||||
|
||||
|
||||
ReplicatedStorage.Assets.Cooldowns:Clone().Parent = model
|
||||
|
||||
|
||||
local HUD_TANK = rHUD.HUD_TANK:Clone()
|
||||
HUD_TANK.Parent = model
|
||||
self.HUD = HUD_TANK
|
||||
|
||||
self:_ResetMass()
|
||||
|
||||
end
|
||||
|
||||
function Bot:DisplayHealth(hp)
|
||||
local self = self :: Bot
|
||||
local Health = self.Components.Health
|
||||
|
||||
|
||||
hp = hp or Health.Health
|
||||
|
||||
local max = Health.MaxHealth
|
||||
@ -116,7 +107,7 @@ function Bot:DisplayHealth(hp)
|
||||
|
||||
local hud = self.HUD
|
||||
hud.Health.ContainerText.HealthText.Text = hp
|
||||
hud.Health.BlackBar.GreenBar.Size = UDim2.new(dif,0,1,0)
|
||||
hud.Health.BlackBar.GreenBar.Size = UDim2.new(dif, 0, 1, 0)
|
||||
end
|
||||
|
||||
function Bot:heal(amount)
|
||||
@ -131,7 +122,6 @@ end
|
||||
|
||||
function Bot:die()
|
||||
self.model:Destroy()
|
||||
|
||||
end
|
||||
|
||||
return Bot
|
||||
|
||||
@ -8,7 +8,7 @@ local localPlayer = Players.LocalPlayer
|
||||
local Data = ReplicatedStorage.Data -- server modules (read-only on client)
|
||||
local GameStates = require(Data.GameState)
|
||||
|
||||
local ClientModules = script.Parent.Modules
|
||||
local ClientModules = script.Parent.Parent.Modules
|
||||
|
||||
local AimRenderer = require(ClientModules.AimRenderer)
|
||||
local InputHandler = require(ClientModules.InputHandler)
|
||||
@ -28,20 +28,16 @@ local turns = 1
|
||||
local PLAYING = "PLAYING"
|
||||
|
||||
function ClientController:Init()
|
||||
|
||||
-- React to server phase changes
|
||||
rev_UpdateGameState.OnClientEvent:Connect(function(phase)
|
||||
shared.Phase = phase
|
||||
if phase == GameStates.LOBBY then
|
||||
|
||||
turns = 1
|
||||
CameraController.ResetCamera()
|
||||
elseif phase == GameStates.GRACE then
|
||||
|
||||
CameraController.WideMapView()
|
||||
BotAbilityUI.NewAbilities()
|
||||
elseif phase == GameStates.AIMING then
|
||||
|
||||
AimRenderer.UnHighlight()
|
||||
|
||||
InputHandler.enable(function(input)
|
||||
@ -50,30 +46,21 @@ function ClientController:Init()
|
||||
InputHandler.disable()
|
||||
BotAbilityUI.DisableAll()
|
||||
AimRenderer.Highlight()
|
||||
|
||||
end,turns)
|
||||
|
||||
end, turns)
|
||||
elseif phase == GameStates.RESOLVING then
|
||||
|
||||
turns += 1
|
||||
BotAbilityUI.HideGUI()
|
||||
InputHandler.disable()
|
||||
AimRenderer.hide()
|
||||
|
||||
elseif phase == "Results" then
|
||||
if localPlayer:HasTag(PLAYING) then
|
||||
BotAbilityUI.HideGUI()
|
||||
CameraController.ResetCamera()
|
||||
end
|
||||
|
||||
|
||||
-- TODO: show win screen
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
return ClientController
|
||||
86
src/shared/client/Controllers/GarageController.luau
Normal file
86
src/shared/client/Controllers/GarageController.luau
Normal file
@ -0,0 +1,86 @@
|
||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||
local GarageUIHandler = require(ReplicatedStorage.Client.Garage.GarageUIHandler)
|
||||
local InventoryClient = require(ReplicatedStorage.Client.Garage.InventoryClient)
|
||||
local TankShowcase = require(ReplicatedStorage.Client.Garage.TankShowcase)
|
||||
local DataTypes = require(ReplicatedStorage.Data.DataTypes)
|
||||
local GarageController = {}
|
||||
|
||||
local tabName
|
||||
local items
|
||||
local index
|
||||
|
||||
local garageShown = false
|
||||
|
||||
local AccessorySlotIndex = 1
|
||||
|
||||
local AccessorySlots = {
|
||||
"Head",
|
||||
"Turret",
|
||||
"Back",
|
||||
}
|
||||
|
||||
function OnItemClick(i: ImageButton)
|
||||
local oldIndex = index
|
||||
index = tonumber(i.Name)
|
||||
|
||||
if oldIndex == index then -- same
|
||||
return
|
||||
end
|
||||
|
||||
local inv = InventoryClient.GetInventory()
|
||||
local itemName = i:GetAttribute("ItemName")
|
||||
|
||||
if tabName == "Tanks" then
|
||||
InventoryClient.SelectTank(itemName)
|
||||
end
|
||||
local currentLoadout = inv.CurrentLoadouts[inv.SelectedTank]
|
||||
|
||||
if tabName == "Skins" then
|
||||
currentLoadout.Skin = i.Name
|
||||
end
|
||||
|
||||
TankShowcase.Showcase(currentLoadout)
|
||||
end
|
||||
|
||||
function OnHandleClicked()
|
||||
garageShown = not garageShown
|
||||
|
||||
if garageShown then
|
||||
GarageController.ShowGarage(true)
|
||||
else
|
||||
GarageController.HideGarage(true)
|
||||
end
|
||||
end
|
||||
|
||||
function OnNavigClick(value) end
|
||||
|
||||
function ChangeItem(index)
|
||||
local currentItem = items[index]
|
||||
end
|
||||
|
||||
function OnTabClicked(tab: TextButton)
|
||||
tabName = tab.Name
|
||||
GarageUIHandler.SetActiveTab(tab)
|
||||
items = InventoryClient.GetItemsByTab(tabName)
|
||||
GarageUIHandler.SetItems(items, tabName)
|
||||
end
|
||||
|
||||
function GarageController.ShowGarage(anim: boolean?)
|
||||
GarageUIHandler.Show(anim)
|
||||
TankShowcase.Focus()
|
||||
TankShowcase.Showcase({ Tank = "Tank" })
|
||||
end
|
||||
|
||||
function GarageController.HideGarage(anim: boolean?)
|
||||
GarageUIHandler.Hide(anim)
|
||||
TankShowcase.UnFocus()
|
||||
end
|
||||
|
||||
function GarageController:Init()
|
||||
GarageUIHandler.Events.OnTabClicked:Connect(OnTabClicked)
|
||||
GarageUIHandler.Events.OnHandleClicked:Connect(OnHandleClicked)
|
||||
|
||||
GarageController.HideGarage(false)
|
||||
end
|
||||
|
||||
return GarageController
|
||||
@ -1,43 +0,0 @@
|
||||
local Players = game:GetService("Players")
|
||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||
local player = Players.LocalPlayer
|
||||
local Garage = workspace.Garage
|
||||
local TankFolder = ReplicatedStorage:WaitForChild("Assets"):WaitForChild("Models"):WaitForChild("Tanks")
|
||||
|
||||
if not game:GetService("RunService"):IsStudio() then
|
||||
TankFolder:WaitForChild("Tank (Testing)"):Destroy()
|
||||
end
|
||||
|
||||
local SelectedTank = player:WaitForChild("SelectedTank")
|
||||
local SelectedSkin = player:WaitForChild("SelectedSkin")
|
||||
local TankDisplay = Garage:WaitForChild("TankDisplay")
|
||||
|
||||
local garage = {}
|
||||
|
||||
local function updateTank()
|
||||
local tankName = SelectedTank.Value
|
||||
local skinName = SelectedSkin.Value
|
||||
local tankModel = TankFolder:FindFirstChild(tankName)
|
||||
if tankModel then
|
||||
local tankBase = tankModel:FindFirstChild("Base")
|
||||
if tankModel and tankBase then
|
||||
local skin = tankModel:FindFirstChild(skinName) or tankModel:FindFirstChild("Default")
|
||||
if skin then
|
||||
TankDisplay:ClearAllChildren()
|
||||
local Tank = tankBase:Clone()
|
||||
Tank.Parent = TankDisplay
|
||||
skin = skin:Clone()
|
||||
skin.Parent = Tank
|
||||
Tank:PivotTo(TankDisplay.CFrame)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function garage:Init()
|
||||
SelectedTank:GetPropertyChangedSignal("Value"):Connect(updateTank)
|
||||
SelectedSkin:GetPropertyChangedSignal("Value"):Connect(updateTank)
|
||||
updateTank()
|
||||
end
|
||||
|
||||
return garage
|
||||
246
src/shared/client/Garage/GarageUIHandler.luau
Normal file
246
src/shared/client/Garage/GarageUIHandler.luau
Normal file
@ -0,0 +1,246 @@
|
||||
local Players = game:GetService("Players")
|
||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||
local StarterGui = game:GetService("StarterGui")
|
||||
local BotData = require(ReplicatedStorage.Data.BotData)
|
||||
local InventoryClient = require(script.Parent.InventoryClient)
|
||||
local DataTypes = require(ReplicatedStorage.Data.DataTypes)
|
||||
local Signal = require(ReplicatedStorage.Shared.SharedUtils.Signal)
|
||||
local sharedBotUtils = require(ReplicatedStorage.Shared.SharedUtils.sharedBotUtils)
|
||||
local GarageUIHandler = {}
|
||||
|
||||
local localPlayer = Players.LocalPlayer
|
||||
local PlayerGui = localPlayer.PlayerGui
|
||||
|
||||
local GarageGui = PlayerGui:WaitForChild("GarageGui")
|
||||
local WorldOverlay = GarageGui:WaitForChild("WorldOverlay")
|
||||
|
||||
local nav_Left = GarageGui.Navigation.Left
|
||||
local nav_Right = GarageGui.Navigation.Right
|
||||
|
||||
local StatusHUD = GarageGui:WaitForChild("StatusHUD")
|
||||
|
||||
local TopCenter = GarageGui:WaitForChild("TopCenter")
|
||||
local BotName = TopCenter:WaitForChild("BotName")
|
||||
local SkinName = TopCenter:WaitForChild("SkinName")
|
||||
|
||||
local Drawer = GarageGui:WaitForChild("Drawer")
|
||||
|
||||
local SelectedTab = Drawer:WaitForChild("SelectedTab")
|
||||
local Selector = SelectedTab:WaitForChild("Selector")
|
||||
|
||||
local Handle = Drawer:WaitForChild("Handle")
|
||||
|
||||
local Tabs = Drawer:WaitForChild("Tabs")
|
||||
local t_Accessories = Tabs:WaitForChild("Accessories")
|
||||
local t_Skins = Tabs:WaitForChild("Skins")
|
||||
local t_Tanks = Tabs:WaitForChild("Tanks")
|
||||
|
||||
local SafetyDrawer = Drawer:WaitForChild("SafetyDrawer")
|
||||
local ScrollingFrame = SafetyDrawer:WaitForChild("ScollingFrame")
|
||||
|
||||
local InteractionContainer = SafetyDrawer:WaitForChild("InteractionContainer")
|
||||
|
||||
local BuyContainer = InteractionContainer:WaitForChild("BuyContainer")
|
||||
local BuyButton = BuyContainer:WaitForChild("LowerButton")
|
||||
local EquipIndicator = BuyContainer:WaitForChild("Indicator")
|
||||
|
||||
local EquipContainer = InteractionContainer:WaitForChild("EquipContainer")
|
||||
local EquipButton = EquipContainer:WaitForChild("LowerButton")
|
||||
local EquipIndicator = EquipContainer:WaitForChild("Indicator")
|
||||
|
||||
type Inventory = DataTypes.Inventory
|
||||
|
||||
local Models = ReplicatedStorage.Assets.Models
|
||||
local ITEM_TEMPLATE = ReplicatedStorage.Assets.UI:WaitForChild("ITEM_TEMPLATE")
|
||||
|
||||
--t is a tab button :)
|
||||
|
||||
local HANDLE_UP_POSITION = UDim2.new(-0.005, 0, 0.773, 0)
|
||||
local HANDLE_DOWN_POSITION = UDim2.new(-0.005, 0, 0.97, 0)
|
||||
|
||||
local _cachedItemTable = {}
|
||||
|
||||
function _moveTabSelector(t: TextButton)
|
||||
print(t.AbsoluteSize)
|
||||
Selector.Size = UDim2.fromOffset(t.AbsoluteSize.X, t.AbsoluteSize.Y)
|
||||
Selector.Position = UDim2.fromOffset(t.AbsolutePosition.X, t.AbsolutePosition.Y)
|
||||
end
|
||||
|
||||
function _cleanUpItems()
|
||||
for _, v in pairs(ScrollingFrame:GetChildren()) do
|
||||
if v:IsA("ImageButton") then
|
||||
v:Destroy()
|
||||
end
|
||||
end
|
||||
table.clear(_cachedItemTable)
|
||||
end
|
||||
|
||||
function _newTemplate(model: Model)
|
||||
local new = ITEM_TEMPLATE:Clone()
|
||||
model.Parent = new.Viewport
|
||||
new.Viewport.CurrentCamera = Instance.new("Camera", new.Viewport)
|
||||
new.Viewport.CurrentCamera.CFrame = CFrame.new(Vector3.new(0, 2, 12), model:GetPivot().Position)
|
||||
new.Parent = ScrollingFrame
|
||||
|
||||
table.insert(_cachedItemTable, new)
|
||||
new.Name = #_cachedItemTable
|
||||
new:SetAttribute("ItemName", model.Name)
|
||||
return new
|
||||
end
|
||||
|
||||
function GarageUIHandler.SetItems(items, currentTab)
|
||||
_cleanUpItems()
|
||||
|
||||
for _, itemModel in items do
|
||||
local ui = _newTemplate(itemModel)
|
||||
|
||||
ui.Activated:Connect(function()
|
||||
GarageUIHandler.Events.OnItemClicked:Fire(itemModel)
|
||||
end)
|
||||
end
|
||||
|
||||
GarageUIHandler.UpdateItemsState(currentTab)
|
||||
end
|
||||
|
||||
function _GetAllItems()
|
||||
return _cachedItemTable
|
||||
end
|
||||
|
||||
function _BrightenItem(item: typeof(ITEM_TEMPLATE))
|
||||
item.BackgroundTransparency = 0
|
||||
end
|
||||
|
||||
function _ShadowItem(item: typeof(ITEM_TEMPLATE))
|
||||
item.BackgroundTransparency = 0.5
|
||||
end
|
||||
|
||||
function _PullHandleUp(anim: boolean?)
|
||||
if anim then
|
||||
Drawer:TweenPosition(HANDLE_UP_POSITION, Enum.EasingDirection.In, Enum.EasingStyle.Linear, 0.3)
|
||||
else
|
||||
Drawer.Position = HANDLE_UP_POSITION
|
||||
end
|
||||
end
|
||||
|
||||
function _PullHandleDown(anim: boolean?)
|
||||
if anim then
|
||||
Drawer:TweenPosition(HANDLE_DOWN_POSITION, Enum.EasingDirection.In, Enum.EasingStyle.Linear, 0.3)
|
||||
else
|
||||
Drawer.Position = HANDLE_DOWN_POSITION
|
||||
end
|
||||
end
|
||||
|
||||
function _TransitionBlack(hide: boolean?)
|
||||
if hide then
|
||||
for i = 0, 100, 5 do
|
||||
local v = i / 100
|
||||
WorldOverlay.BackgroundTransparency = v
|
||||
task.wait(0.03)
|
||||
end
|
||||
else
|
||||
for i = 100, 0, -5 do
|
||||
local v = i / 100
|
||||
WorldOverlay.BackgroundTransparency = v
|
||||
task.wait(0.03)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function _ToggleVisibility(value: boolean?)
|
||||
value = value or false
|
||||
StatusHUD.Visible = value
|
||||
Drawer.Controls.Visible = value
|
||||
GarageGui.AbilityHUD.Visible = value
|
||||
StatusHUD.Visible = value
|
||||
TopCenter.Visible = value
|
||||
end
|
||||
|
||||
function GarageUIHandler.Hide(anim: boolean?)
|
||||
_PullHandleDown(anim)
|
||||
if anim then
|
||||
_TransitionBlack(false)
|
||||
end
|
||||
_ToggleVisibility(false)
|
||||
if anim then
|
||||
_TransitionBlack(true)
|
||||
end
|
||||
end
|
||||
|
||||
function GarageUIHandler.Show(anim: boolean?)
|
||||
_PullHandleUp(anim)
|
||||
if anim then
|
||||
_TransitionBlack(false)
|
||||
end
|
||||
_ToggleVisibility(true)
|
||||
if anim then
|
||||
_TransitionBlack(true)
|
||||
end
|
||||
end
|
||||
|
||||
function GarageUIHandler.SetActiveTab(t: TextButton)
|
||||
_moveTabSelector(t)
|
||||
end
|
||||
|
||||
function GarageUIHandler.UpdateItemsState(tabName)
|
||||
for _, item in pairs(_GetAllItems()) do
|
||||
local itemName = item:GetAttribute("ItemName")
|
||||
local can = true
|
||||
if tabName == "Tanks" then
|
||||
can = InventoryClient.HasTank(itemName)
|
||||
elseif tabName == "Skins" then
|
||||
can = InventoryClient.HasSkin(itemName)
|
||||
end
|
||||
|
||||
if can then
|
||||
_BrightenItem(item)
|
||||
else
|
||||
_ShadowItem(item)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
GarageUIHandler.Messages = {
|
||||
CANT_AFFORD = "Not Enough Money",
|
||||
CAN_AFFORD = "BUY",
|
||||
|
||||
CAN_EQUIP = "EQUIP",
|
||||
ALREADY_EQUIPPED = "EQUIPPED",
|
||||
|
||||
ERROR = "Something went wrong",
|
||||
}
|
||||
|
||||
GarageUIHandler.Events = {
|
||||
OnTabClicked = Signal.new(),
|
||||
OnItemClicked = Signal.new(),
|
||||
OnBuyClicked = Signal.new(),
|
||||
OnEquipClicked = Signal.new(),
|
||||
OnNavigClicked = Signal.new(),
|
||||
OnHandleClicked = Signal.new(),
|
||||
}
|
||||
|
||||
function GarageUIHandler:Init()
|
||||
for _, v in pairs(Tabs:GetChildren()) do
|
||||
if not v:IsA("TextButton") then
|
||||
continue
|
||||
end
|
||||
v.Activated:Connect(function()
|
||||
_moveTabSelector(v)
|
||||
GarageUIHandler.Events.OnTabClicked:Fire(v)
|
||||
end)
|
||||
end
|
||||
|
||||
nav_Left.Activated:Connect(function()
|
||||
GarageUIHandler.Events.OnNavigClicked:Fire(-1)
|
||||
end)
|
||||
nav_Right.Activated:Connect(function()
|
||||
GarageUIHandler.Events.OnNavigClicked:Fire(1)
|
||||
end)
|
||||
EquipButton.Activated:Connect(function()
|
||||
GarageUIHandler.Events.OnEquipClicked:Fire(EquipButton)
|
||||
end)
|
||||
Handle.Activated:Connect(function()
|
||||
GarageUIHandler.Events.OnHandleClicked:Fire()
|
||||
end)
|
||||
end
|
||||
|
||||
return GarageUIHandler
|
||||
87
src/shared/client/Garage/InventoryClient.luau
Normal file
87
src/shared/client/Garage/InventoryClient.luau
Normal file
@ -0,0 +1,87 @@
|
||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||
local DataTypes = require(ReplicatedStorage.Data.DataTypes)
|
||||
local sharedBotUtils = require(ReplicatedStorage.Shared.SharedUtils.sharedBotUtils)
|
||||
local InventoryClient = {}
|
||||
|
||||
InventoryClient.Inventory = {}
|
||||
|
||||
local Models = ReplicatedStorage.Assets.Models
|
||||
local Remote = ReplicatedStorage.Remote
|
||||
local rfn_Inventory = Remote.InventoryService
|
||||
|
||||
local _inv = nil
|
||||
|
||||
function InventoryClient.Pull(): DataTypes.Inventory
|
||||
local oldTank = _inv and _inv.SelectedTank
|
||||
|
||||
local new_inv = rfn_Inventory:InvokeServer("GetInventory")
|
||||
|
||||
if not new_inv then
|
||||
return _inv
|
||||
end
|
||||
|
||||
if oldTank then
|
||||
new_inv.SelectedTank = oldTank
|
||||
end
|
||||
|
||||
_inv = new_inv
|
||||
return _inv
|
||||
end
|
||||
|
||||
function InventoryClient.GetItemsByTab(tabName)
|
||||
local inv = InventoryClient.GetInventory()
|
||||
|
||||
local items = {}
|
||||
|
||||
if tabName == "Tanks" then
|
||||
for _, v in pairs(Models.Tanks:GetChildren()) do
|
||||
local currentTankName = v.Name
|
||||
local currentLoadout = inv.CurrentLoadouts[currentTankName]
|
||||
|
||||
local newModel = sharedBotUtils.CreateBot(currentLoadout, currentTankName)
|
||||
table.insert(items, newModel)
|
||||
end
|
||||
elseif tabName == "Skins" then
|
||||
local tankSelected = InventoryClient.GetTankSelected()
|
||||
local currentTankFolder = Models.Tanks:FindFirstChild(tankSelected)
|
||||
|
||||
for _, v in pairs(currentTankFolder.Skins:GetChildren()) do
|
||||
local currentLoadout = {
|
||||
Skin = v.Name,
|
||||
Tank = tankSelected,
|
||||
}
|
||||
|
||||
local newModel = sharedBotUtils.CreateBot(currentLoadout, tankSelected)
|
||||
table.insert(items, newModel)
|
||||
end
|
||||
end
|
||||
return items
|
||||
end
|
||||
|
||||
function InventoryClient.GetTankSelected()
|
||||
return InventoryClient.GetInventory().SelectedTank
|
||||
end
|
||||
|
||||
function InventoryClient.SelectTank(tank)
|
||||
local inv = InventoryClient.GetInventory()
|
||||
inv.SelectedTank = tank
|
||||
inv.CurrentLoadouts[tank].Tank = tank
|
||||
end
|
||||
|
||||
function InventoryClient.HasTank(tank)
|
||||
return table.find(InventoryClient.GetInventory().Tanks, tank)
|
||||
end
|
||||
|
||||
function InventoryClient.HasSkin(skin)
|
||||
local tank = InventoryClient.GetTankSelected()
|
||||
return table.find(InventoryClient.GetInventory().Skins[tank], skin)
|
||||
end
|
||||
|
||||
function InventoryClient.GetInventory(): DataTypes.Inventory
|
||||
if not _inv then
|
||||
return InventoryClient.Pull()
|
||||
end
|
||||
return _inv
|
||||
end
|
||||
|
||||
return InventoryClient
|
||||
68
src/shared/client/Garage/TankShowcase.luau
Normal file
68
src/shared/client/Garage/TankShowcase.luau
Normal file
@ -0,0 +1,68 @@
|
||||
local Players = game:GetService("Players")
|
||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||
local DataTypes = require(ReplicatedStorage.Data.DataTypes)
|
||||
local sharedBotUtils = require(ReplicatedStorage.Shared.SharedUtils.sharedBotUtils)
|
||||
|
||||
local TankShowcase = {}
|
||||
|
||||
local GarageFolder = workspace.Garage
|
||||
local TankDisplay = GarageFolder:WaitForChild("TankDisplay")
|
||||
|
||||
local cCamera = workspace.CurrentCamera
|
||||
|
||||
local rotOffset = CFrame.Angles(0, 0, 0)
|
||||
local currentTank
|
||||
|
||||
--[[
|
||||
Anim
|
||||
2:From right to center
|
||||
1:From left to center
|
||||
0:No Anim
|
||||
|
||||
|
||||
]]
|
||||
|
||||
function _cleanupTank()
|
||||
if currentTank then
|
||||
currentTank:Destroy()
|
||||
currentTank = nil
|
||||
end
|
||||
end
|
||||
|
||||
function _resetRot()
|
||||
rotOffset = CFrame.Angles(0, 0, 0)
|
||||
end
|
||||
|
||||
function TankShowcase.Focus()
|
||||
cCamera.CameraType = Enum.CameraType.Scriptable
|
||||
local position = TankDisplay.Position + TankDisplay.CFrame:VectorToWorldSpace(Vector3.new(0, 0, 10))
|
||||
local newCFrame = CFrame.lookAt(position, TankDisplay.Position)
|
||||
cCamera.CFrame = newCFrame
|
||||
end
|
||||
|
||||
function TankShowcase.UnFocus()
|
||||
cCamera.CameraType = Enum.CameraType.Custom
|
||||
end
|
||||
|
||||
function TankShowcase.Showcase(loadout: DataTypes.Loadout)
|
||||
_cleanupTank()
|
||||
local new = sharedBotUtils.CreateBot(loadout)
|
||||
print(new)
|
||||
new:PivotTo(TankDisplay.CFrame)
|
||||
_resetRot()
|
||||
currentTank = new
|
||||
currentTank.Parent = workspace
|
||||
end
|
||||
|
||||
function TankShowcase.Update(dt)
|
||||
if currentTank then
|
||||
rotOffset = rotOffset * CFrame.Angles(0, math.rad(dt), 0)
|
||||
currentTank:PivotTo(CFrame.new(currentTank:GetPivot().Position) * rotOffset)
|
||||
else
|
||||
_resetRot()
|
||||
end
|
||||
end
|
||||
|
||||
function TankShowcase:Init() end
|
||||
|
||||
return TankShowcase
|
||||
@ -1,22 +0,0 @@
|
||||
local GarageUIController = {}
|
||||
|
||||
local localPlayer = game.Players.LocalPlayer
|
||||
|
||||
local PlayerGui = localPlayer.PlayerGui
|
||||
local GarageGui = PlayerGui:WaitForChild("GarageGui")
|
||||
|
||||
function GarageUIController.OpenGUI()
|
||||
|
||||
end
|
||||
|
||||
function GarageUIController.CloseGUI()
|
||||
|
||||
end
|
||||
|
||||
|
||||
function GarageUIController:Init()
|
||||
|
||||
end
|
||||
|
||||
|
||||
return GarageUIController
|
||||
@ -1,22 +1,34 @@
|
||||
|
||||
local ServerScriptService = game:GetService("ServerScriptService")
|
||||
export type FireData = {
|
||||
weapon : string,
|
||||
angle : number,
|
||||
power : number,
|
||||
origin : Vector3,
|
||||
weapon: string,
|
||||
angle: number,
|
||||
power: number,
|
||||
origin: Vector3,
|
||||
|
||||
specialArgs : {any}
|
||||
specialArgs: { any },
|
||||
}
|
||||
|
||||
export type VoteOptionData = {
|
||||
Name : string,
|
||||
DisplayName : string,
|
||||
DisplayImage : string
|
||||
Name: string,
|
||||
DisplayName: string,
|
||||
DisplayImage: string,
|
||||
}
|
||||
|
||||
export type Loadout = {
|
||||
Skin : string,
|
||||
HeadAccessory : string?,
|
||||
Tank: string,
|
||||
Skin: string?,
|
||||
HeadAccessory: string?,
|
||||
Name: string?,
|
||||
Locked: boolean?, -- can be edited
|
||||
}
|
||||
|
||||
export type Inventory = {
|
||||
Tanks: { string }, -- tanks the player owns
|
||||
Skins: { [string]: { string } }, -- skins per tank
|
||||
Loadouts: { [string]: { [string]: Loadout } },
|
||||
CurrentLoadouts: { [string]: Loadout }, -- Current loadout of tank
|
||||
|
||||
SelectedTank: string,
|
||||
}
|
||||
|
||||
return {}
|
||||
0
src/shared/data/SkinData.luau
Normal file
0
src/shared/data/SkinData.luau
Normal file
@ -1,161 +0,0 @@
|
||||
--!strict
|
||||
-- Partial types for Promise
|
||||
|
||||
local Packages = script.Parent.Packages
|
||||
local Promise: any = if Packages:FindFirstChild("Promise") then require(Packages.Promise) else nil
|
||||
|
||||
export type Status = "Started" | "Resolved" | "Rejected" | "Cancelled"
|
||||
export type ErrorKind = "ExecutionError" | "AlreadyCancelled" | "NotResolvedInTime" | "TimedOut"
|
||||
|
||||
type ErrorStaticAndShared = {
|
||||
Kind: {
|
||||
ExecutionError: "ExecutionError",
|
||||
AlreadyCancelled: "AlreadyCancelled",
|
||||
NotResolvedInTime: "NotResolvedInTime",
|
||||
TimedOut: "TimedOut",
|
||||
},
|
||||
}
|
||||
type ErrorOptions = {
|
||||
error: string,
|
||||
trace: string?,
|
||||
context: string?,
|
||||
kind: ErrorKind,
|
||||
}
|
||||
|
||||
export type Error = typeof(setmetatable(
|
||||
{} :: ErrorStaticAndShared & {
|
||||
error: string,
|
||||
trace: string?,
|
||||
context: string?,
|
||||
kind: ErrorKind,
|
||||
parent: Error?,
|
||||
createdTick: number,
|
||||
createdTrace: string,
|
||||
|
||||
extend: (self: Error, options: ErrorOptions?) -> Error,
|
||||
getErrorChain: (self: Error) -> {Error},
|
||||
},
|
||||
{} :: {__tostring: (self: Error) -> string}
|
||||
))
|
||||
type ErrorStatic = ErrorStaticAndShared & {
|
||||
new: (options: ErrorOptions?, parent: Error?) -> Error,
|
||||
is: (anything: any) -> boolean,
|
||||
isKind: (anything: any, kind: ErrorKind) -> boolean,
|
||||
}
|
||||
|
||||
export type Promise = {
|
||||
andThen: (
|
||||
self: Promise,
|
||||
successHandler: (...any) -> ...any,
|
||||
failureHandler: ((...any) -> ...any)?
|
||||
) -> Promise,
|
||||
andThenCall: <TArgs...>(self: Promise, callback: (TArgs...) -> ...any, TArgs...) -> any,
|
||||
andThenReturn: (self: Promise, ...any) -> Promise,
|
||||
|
||||
await: (self: Promise) -> (boolean, ...any),
|
||||
awaitStatus: (self: Promise) -> (Status, ...any),
|
||||
|
||||
cancel: (self: Promise) -> (),
|
||||
catch: (self: Promise, failureHandler: (...any) -> ...any) -> Promise,
|
||||
expect: (self: Promise) -> ...any,
|
||||
|
||||
finally: (self: Promise, finallyHandler: (status: Status) -> ...any) -> Promise,
|
||||
finallyCall: <TArgs...>(self: Promise, callback: (TArgs...) -> ...any, TArgs...) -> Promise,
|
||||
finallyReturn: (self: Promise, ...any) -> Promise,
|
||||
|
||||
getStatus: (self: Promise) -> Status,
|
||||
now: (self: Promise, rejectionValue: any?) -> Promise,
|
||||
tap: (self: Promise, tapHandler: (...any) -> ...any) -> Promise,
|
||||
timeout: (self: Promise, seconds: number, rejectionValue: any?) -> Promise,
|
||||
}
|
||||
export type TypedPromise<T...> = {
|
||||
andThen: (self: Promise, successHandler: (T...) -> ...any, failureHandler: ((...any) -> ...any)?) -> Promise,
|
||||
andThenCall: <TArgs...>(self: Promise, callback: (TArgs...) -> ...any, TArgs...) -> Promise,
|
||||
andThenReturn: (self: Promise, ...any) -> Promise,
|
||||
|
||||
await: (self: Promise) -> (boolean, T...),
|
||||
awaitStatus: (self: Promise) -> (Status, T...),
|
||||
|
||||
cancel: (self: Promise) -> (),
|
||||
catch: (self: Promise, failureHandler: (...any) -> ...any) -> Promise,
|
||||
expect: (self: Promise) -> T...,
|
||||
|
||||
finally: (self: Promise, finallyHandler: (status: Status) -> ...any) -> Promise,
|
||||
finallyCall: <TArgs...>(self: Promise, callback: (TArgs...) -> ...any, TArgs...) -> Promise,
|
||||
finallyReturn: (self: Promise, ...any) -> Promise,
|
||||
|
||||
getStatus: (self: Promise) -> Status,
|
||||
now: (self: Promise, rejectionValue: any?) -> Promise,
|
||||
tap: (self: Promise, tapHandler: (T...) -> ...any) -> Promise,
|
||||
timeout: (self: Promise, seconds: number, rejectionValue: any?) -> TypedPromise<T...>,
|
||||
}
|
||||
|
||||
type Signal<T...> = {
|
||||
Connect: (self: Signal<T...>, callback: (T...) -> ...any) -> SignalConnection,
|
||||
}
|
||||
|
||||
type SignalConnection = {
|
||||
Disconnect: (self: SignalConnection) -> ...any,
|
||||
[any]: any,
|
||||
}
|
||||
export type PromiseStatic = {
|
||||
Error: ErrorStatic,
|
||||
Status: {
|
||||
Started: "Started",
|
||||
Resolved: "Resolved",
|
||||
Rejected: "Rejected",
|
||||
Cancelled: "Cancelled",
|
||||
},
|
||||
|
||||
all: <T>(promises: {TypedPromise<T>}) -> TypedPromise<{T}>,
|
||||
allSettled: <T>(promise: {TypedPromise<T>}) -> TypedPromise<{Status}>,
|
||||
any: <T>(promise: {TypedPromise<T>}) -> TypedPromise<T>,
|
||||
defer: <TReturn...>(
|
||||
executor: (
|
||||
resolve: (TReturn...) -> (),
|
||||
reject: (...any) -> (),
|
||||
onCancel: (abortHandler: (() -> ())?) -> boolean
|
||||
) -> ()
|
||||
) -> TypedPromise<TReturn...>,
|
||||
delay: (seconds: number) -> TypedPromise<number>,
|
||||
each: <T, TReturn>(
|
||||
list: {T | TypedPromise<T>},
|
||||
predicate: (value: T, index: number) -> TReturn | TypedPromise<TReturn>
|
||||
) -> TypedPromise<{TReturn}>,
|
||||
fold: <T, TReturn>(
|
||||
list: {T | TypedPromise<T>},
|
||||
reducer: (accumulator: TReturn, value: T, index: number) -> TReturn | TypedPromise<TReturn>
|
||||
) -> TypedPromise<TReturn>,
|
||||
fromEvent: <TReturn...>(
|
||||
event: Signal<TReturn...>,
|
||||
predicate: ((TReturn...) -> boolean)?
|
||||
) -> TypedPromise<TReturn...>,
|
||||
is: (object: any) -> boolean,
|
||||
new: <TReturn...>(
|
||||
executor: (
|
||||
resolve: (TReturn...) -> (),
|
||||
reject: (...any) -> (),
|
||||
onCancel: (abortHandler: (() -> ())?) -> boolean
|
||||
) -> ()
|
||||
) -> TypedPromise<TReturn...>,
|
||||
onUnhandledRejection: (callback: (promise: TypedPromise<any>, ...any) -> ()) -> () -> (),
|
||||
promisify: <TArgs..., TReturn...>(callback: (TArgs...) -> TReturn...) -> (TArgs...) -> TypedPromise<TReturn...>,
|
||||
race: <T>(promises: {TypedPromise<T>}) -> TypedPromise<T>,
|
||||
reject: (...any) -> TypedPromise<...any>,
|
||||
resolve: <TReturn...>(TReturn...) -> TypedPromise<TReturn...>,
|
||||
retry: <TArgs..., TReturn...>(
|
||||
callback: (TArgs...) -> TypedPromise<TReturn...>,
|
||||
times: number,
|
||||
TArgs...
|
||||
) -> TypedPromise<TReturn...>,
|
||||
retryWithDelay: <TArgs..., TReturn...>(
|
||||
callback: (TArgs...) -> TypedPromise<TReturn...>,
|
||||
times: number,
|
||||
seconds: number,
|
||||
TArgs...
|
||||
) -> TypedPromise<TReturn...>,
|
||||
some: <T>(promise: {TypedPromise<T>}, count: number) -> TypedPromise<{T}>,
|
||||
try: <TArgs..., TReturn...>(callback: (TArgs...) -> TReturn..., TArgs...) -> TypedPromise<TReturn...>,
|
||||
}
|
||||
|
||||
return Promise :: PromiseStatic?
|
||||
@ -1,14 +1,19 @@
|
||||
local sharedBotUtils = {}
|
||||
|
||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||
local Assets = ReplicatedStorage.Assets
|
||||
local Models = Assets.Models
|
||||
local TankFolder = Models.Tanks :: Folder
|
||||
|
||||
local rData = ReplicatedStorage.Data
|
||||
local DataTypes = require(ReplicatedStorage.Data.DataTypes)
|
||||
local BotData = require(rData.BotData)
|
||||
local GameState = require(rData.GameState)
|
||||
local WeaponData = require(rData.WeaponData)
|
||||
|
||||
local sUtils = ReplicatedStorage.Shared.SharedUtils
|
||||
local TwoDimensionUtils = require(sUtils.TwoDimensionUtils)
|
||||
local WeldModule = require(script.Parent.WeldModule)
|
||||
|
||||
local wBots = workspace.Bots
|
||||
|
||||
@ -19,32 +24,97 @@ local NO_COOLDOWN = {
|
||||
|
||||
local COOLDOWN_ABILITY = {
|
||||
NormalAbility = 2,
|
||||
SpecialAbility = 3
|
||||
SpecialAbility = 3,
|
||||
}
|
||||
|
||||
function sharedBotUtils.GetBotData(UserId : number) : BotData.BotData
|
||||
--Not cloned
|
||||
function sharedBotUtils.GetTankBase(name): Model
|
||||
name = name or "Tank"
|
||||
|
||||
local currentFolder = TankFolder:FindFirstChild(name)
|
||||
|
||||
if not currentFolder then
|
||||
currentFolder = TankFolder.Tank
|
||||
end
|
||||
|
||||
local base = currentFolder:FindFirstChild("Base")
|
||||
if not base then
|
||||
base = currentFolder.Base
|
||||
end
|
||||
|
||||
return base
|
||||
end
|
||||
--Not cloned
|
||||
function sharedBotUtils.GetTankSkin(name, skinName): Model
|
||||
name = name or "Tank"
|
||||
|
||||
local currentFolder = TankFolder:FindFirstChild(name)
|
||||
if not currentFolder then
|
||||
currentFolder = TankFolder.Tank
|
||||
end
|
||||
|
||||
skinName = skinName or "Default"
|
||||
|
||||
local skin = currentFolder.Skins:FindFirstChild(name)
|
||||
if not skin then
|
||||
skin = currentFolder.Skins.Default
|
||||
end
|
||||
|
||||
return skin
|
||||
end
|
||||
|
||||
function sharedBotUtils.CreateBot(loadout: DataTypes.Loadout, tankName: string?)
|
||||
if not loadout then
|
||||
loadout = { Name = "Whatever", Tank = "Tank" }
|
||||
end
|
||||
if not tankName then
|
||||
tankName = loadout.Tank
|
||||
end
|
||||
|
||||
local base = sharedBotUtils.GetTankBase(tankName):Clone()
|
||||
local skin = sharedBotUtils.GetTankSkin(tankName, loadout.Skin):Clone()
|
||||
|
||||
skin.Name = "Skin"
|
||||
base.Name = "Base"
|
||||
|
||||
local newTank = Instance.new("Model")
|
||||
skin.Parent = newTank
|
||||
base.Parent = newTank
|
||||
newTank.PrimaryPart = base.PrimaryPart
|
||||
|
||||
WeldModule.UnWeldModel(newTank)
|
||||
WeldModule.WeldModel(newTank)
|
||||
|
||||
return newTank
|
||||
end
|
||||
|
||||
function sharedBotUtils.GetBotData(UserId: number): BotData.BotData
|
||||
local model = sharedBotUtils.FindBotModel(UserId)
|
||||
return BotData[model:GetAttribute("Type")]
|
||||
end
|
||||
|
||||
function sharedBotUtils.GetWeaponName(UserId : number,weaponType : string)
|
||||
function sharedBotUtils.GetWeaponName(UserId: number, weaponType: string)
|
||||
local bData = sharedBotUtils.GetBotData(UserId)
|
||||
local name = bData.weapons[weaponType] or weaponType
|
||||
|
||||
return name
|
||||
end
|
||||
|
||||
function sharedBotUtils.GetTurret(UserId : number) : Model
|
||||
function sharedBotUtils.GetTurret(UserId: number): Model
|
||||
local model = sharedBotUtils.FindBotModel(UserId)
|
||||
return model.RotatePart
|
||||
end
|
||||
|
||||
function sharedBotUtils.RotateTurret(UserId : number,launchDirection)
|
||||
function sharedBotUtils.RotateTurret(UserId: number, launchDirection)
|
||||
local turret = sharedBotUtils.GetTurret(UserId)
|
||||
if not turret then return end
|
||||
if not turret then
|
||||
return
|
||||
end
|
||||
|
||||
local weld = turret.PrimaryPart:FindFirstChildOfClass("Weld")
|
||||
if not weld then return end
|
||||
if not weld then
|
||||
return
|
||||
end
|
||||
|
||||
local base = weld.Part0
|
||||
local turretPart = weld.Part1
|
||||
@ -57,7 +127,7 @@ function sharedBotUtils.RotateTurret(UserId : number,launchDirection)
|
||||
weld.C0 = base.CFrame:Inverse() * worldCF
|
||||
end
|
||||
|
||||
function sharedBotUtils.GetWeaponsData(UserId : number) : {[string] : WeaponData.WeaponData}
|
||||
function sharedBotUtils.GetWeaponsData(UserId: number): { [string]: WeaponData.WeaponData }
|
||||
local tbl = {}
|
||||
|
||||
local bData = sharedBotUtils.GetBotData(UserId)
|
||||
@ -65,21 +135,21 @@ function sharedBotUtils.GetWeaponsData(UserId : number) : {[string] : WeaponData
|
||||
tbl.Move = WeaponData.Move
|
||||
tbl.Missile = WeaponData.Missile
|
||||
|
||||
for weaponType,weaponName in pairs(bData.weapons) do
|
||||
for weaponType, weaponName in pairs(bData.weapons) do
|
||||
local wData = WeaponData[weaponName]
|
||||
tbl[weaponType] = wData
|
||||
end
|
||||
return tbl
|
||||
end
|
||||
function sharedBotUtils.GetBotPosition(UserId : number)
|
||||
function sharedBotUtils.GetBotPosition(UserId: number)
|
||||
local model = sharedBotUtils.FindBotModel(UserId)
|
||||
return model.PrimaryPart.Position
|
||||
end
|
||||
function sharedBotUtils.CanUseAbility(UserId : number, abilityType : string)
|
||||
function sharedBotUtils.CanUseAbility(UserId: number, abilityType: string)
|
||||
if true then
|
||||
return true
|
||||
end
|
||||
local isInCooldown = sharedBotUtils.AbilityInCooldown(UserId,abilityType)
|
||||
local isInCooldown = sharedBotUtils.AbilityInCooldown(UserId, abilityType)
|
||||
if isInCooldown then
|
||||
return
|
||||
end
|
||||
@ -91,21 +161,21 @@ function sharedBotUtils.CanUseAbility(UserId : number, abilityType : string)
|
||||
return true
|
||||
end
|
||||
|
||||
function sharedBotUtils.GetCooldowns(UserId : number) : Folder
|
||||
function sharedBotUtils.GetCooldowns(UserId: number): Folder
|
||||
local model = sharedBotUtils.FindBotModel(UserId)
|
||||
|
||||
return model.Cooldowns
|
||||
end
|
||||
|
||||
function sharedBotUtils.AbilityInCooldown(UserId,ability : string)
|
||||
function sharedBotUtils.AbilityInCooldown(UserId, ability: string)
|
||||
if NO_COOLDOWN[ability] then
|
||||
return
|
||||
end
|
||||
|
||||
local cooldown = sharedBotUtils.GetCooldowns(UserId)
|
||||
local cAbil : IntValue = cooldown:FindFirstChild(ability)
|
||||
local cAbil: IntValue = cooldown:FindFirstChild(ability)
|
||||
if not cAbil then
|
||||
warn("Ability name " .. ability .." doesnt match with any of the cooldown values, what the hell did you do?")
|
||||
warn("Ability name " .. ability .. " doesnt match with any of the cooldown values, what the hell did you do?")
|
||||
print(cooldown)
|
||||
return
|
||||
end
|
||||
@ -113,13 +183,13 @@ function sharedBotUtils.AbilityInCooldown(UserId,ability : string)
|
||||
return COOLDOWN_ABILITY[ability] ~= cAbil.Value
|
||||
end
|
||||
|
||||
function sharedBotUtils.FindBotModel(UserId : number) : Model
|
||||
function sharedBotUtils.FindBotModel(UserId: number): Model
|
||||
local b = wBots:WaitForChild(tostring(UserId))
|
||||
|
||||
return b
|
||||
end
|
||||
|
||||
function sharedBotUtils.GetShootPos(UserId : number)
|
||||
function sharedBotUtils.GetShootPos(UserId: number)
|
||||
local turret = sharedBotUtils.GetTurret(UserId)
|
||||
if not turret then
|
||||
warn("no turret model >:(")
|
||||
@ -134,13 +204,15 @@ function sharedBotUtils.GetShootPos(UserId : number)
|
||||
return Shoot.PrimaryPart.Position
|
||||
end
|
||||
|
||||
function sharedBotUtils.GetBotRoot(UserId : number) : Part
|
||||
function sharedBotUtils.GetBotRoot(UserId: number): Part
|
||||
local b = sharedBotUtils.FindBotModel(UserId)
|
||||
return b.PrimaryPart
|
||||
end
|
||||
function sharedBotUtils.IsObjectOnFloor(model) : boolean
|
||||
function sharedBotUtils.IsObjectOnFloor(model): boolean
|
||||
local root = model.PrimaryPart
|
||||
if not root then return false end
|
||||
if not root then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Get the model size (assumes the root's parent is the full model)
|
||||
local model = root.Parent
|
||||
@ -152,7 +224,7 @@ function sharedBotUtils.IsObjectOnFloor(model) : boolean
|
||||
|
||||
local params = RaycastParams.new()
|
||||
params.FilterType = Enum.RaycastFilterType.Exclude
|
||||
params.FilterDescendantsInstances = {model}
|
||||
params.FilterDescendantsInstances = { model }
|
||||
|
||||
local result = workspace:Raycast(origin, direction, params)
|
||||
if not result then
|
||||
@ -166,7 +238,7 @@ function sharedBotUtils.IsObjectOnFloor(model) : boolean
|
||||
-- must be close to ground and not moving vertically
|
||||
return distance <= (size.Y / 2 + 0.5) and math.abs(velY) < 1
|
||||
end
|
||||
function sharedBotUtils.GetBotVelocity(UserId : number)
|
||||
function sharedBotUtils.GetBotVelocity(UserId: number)
|
||||
local root = sharedBotUtils.GetBotRoot(UserId)
|
||||
return root.AssemblyLinearVelocity
|
||||
end
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user