Skip to content

Commit

Permalink
Merge pull request #2 from prooheckcp/development
Browse files Browse the repository at this point in the history
Version 0.1.0 [BETA]
  • Loading branch information
prooheckcp committed May 16, 2024
2 parents 239c221 + 1f0f715 commit 13466ba
Show file tree
Hide file tree
Showing 27 changed files with 456 additions and 56 deletions.
50 changes: 39 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,45 @@
# RoQuest
Generated by [Rojo](https://github.com/rojo-rbx/rojo) 6.2.0.
![](./gh-assets/FullLogo.png)

## Getting Started
To build the place from scratch, use:
RoQuest is a sophisticated abstract implementation of a Quest System. Although it can self manage nearly everything it also allows for the developer to manually set and manage everything

![](./gh-assets/CollectExample.gif)
![](./gh-assets/CollectCorn.gif)

## 📦 Installing

RoQuest can be installed in different ways, depending on your project's needs. Choose the method that suits you best:

```bash
rojo build -o "RoQuest.rbxlx"
```

Next, open `RoQuest.rbxlx` in Roblox Studio and start the Rojo server:
### 🐶 Wally
<a href="https://wally.run/package/prooheckcp/RoQuest">Watch wally's page</a>

```bash
rojo serve
```
roquest = "prooheckcp/roquest@>0.0.0, <10.0.0"
```


### 🔨Studio
<a href="https://create.roblox.com/store/asset/17475376719">Get the Roblox Model</a>


### 🐙GitHub
<a href="https://github.com/prooheckcp/RoQuest/releases">Download from Github Releases</a>


# 📚Start Learning

You can start learning in the docs page! https://prooheckcp.github.io/RoQuest/docs/intro

## 📷Tutorial
Coming soon!

# ⭐ Contributing
Please leave a star on [GitHub](https://github.com/prooheckcp/RoQuest), it helps a lot!

Pull requests are welcome. For major changes, please open an issue first
to discuss what you would like to change.

Please make sure to update tests as appropriate.

For more help, check out [the Rojo documentation](https://rojo.space/docs).
# 📄 License
[MIT](https://choosealicense.com/licenses/mit/)
4 changes: 2 additions & 2 deletions RoQuest/Client/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,8 @@ function RoQuestClient:Init(lifeCycles: {QuestLifeCycle}?): ()
self:_ChangeUnAvailableState(questId, true)
end

self:_OnPlayerDataChanged(net:Call("GetPlayerData"):Await())

net:On("OnPlayerDataChanged", function(playerQuestData: PlayerQuestData)
self:_OnPlayerDataChanged(playerQuestData)
end)
Expand Down Expand Up @@ -542,8 +544,6 @@ function RoQuestClient:Init(lifeCycles: {QuestLifeCycle}?): ()
self:_OnQuestUnavailable(questId)
end)

self:_OnPlayerDataChanged(net:Call("GetPlayerData"):Await())

task.spawn(function()
while not self._PlayerQuestData do
task.wait()
Expand Down
26 changes: 14 additions & 12 deletions RoQuest/Server/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -468,12 +468,12 @@ function RoQuestServer:Init(quests: {Quest}, lifeCycles: {QuestLifeCycle}?): ()
local net = Red.Server("QuestNamespace", {
"OnQuestObjectiveChanged",
"OnQuestCancelled",
"OnQuestStarted",
"OnQuestDelivered",
"OnQuestCompleted",
"OnQuestAvailable",
"OnQuestUnavailable",
"OnPlayerDataChanged",
"OnQuestStarted",
"OnQuestAvailable",
})

self.OnQuestObjectiveChanged:Connect(function(player: Player, questId: string, objectiveId: string, newAmount: number)
Expand Down Expand Up @@ -1381,7 +1381,7 @@ function RoQuestServer:_QuestBecameAvailable(questId: string): ()
self._StaticAvailableQuests[questId] = true -- Should give to players if possible

for player: Player in self._Quests do
self:_NewPlayerAvailableQuest(player, questId)
task.delay(0, self._NewPlayerAvailableQuest, self, player, questId)
end
end

Expand Down Expand Up @@ -1454,6 +1454,7 @@ function RoQuestServer:_GiveQuest(player: Player, questId: string, questProgress
if quest then -- We are repeating the quest!!!
questProgress = quest:_GetQuestProgress()
questProgress.QuestObjectiveProgresses = questObjectiveProgresses
questProgress.QuestStatus = QuestStatus.InProgress
end
end

Expand All @@ -1464,7 +1465,6 @@ function RoQuestServer:_GiveQuest(player: Player, questId: string, questProgress
end
end

questProgress.QuestStatus = QuestStatus.InProgress
questClone:_SetQuestProgress(questProgress)
else
questClone:_SetQuestProgress(QuestProgress {
Expand All @@ -1476,16 +1476,17 @@ function RoQuestServer:_GiveQuest(player: Player, questId: string, questProgress
})
end

for _, lifeCycleName: string in questClone.LifeCycles do
self:_CreateLifeCycle(player, questClone, lifeCycleName)
end

self._AvailableQuests[player][questId] = nil
self._Quests[player][questId] = questClone
self._PlayerQuestData[player].InProgress[questId] = questClone:_GetQuestProgress()
self._PlayerQuestData[player].Delivered[questId] = nil
self._PlayerQuestData[player][questClone:GetQuestStatus()][questId] = questClone:_GetQuestProgress()
self._Troves[player]:Add(questClone)

for _, lifeCycleName: string in questClone.LifeCycles do
self:_CreateLifeCycle(player, questClone, lifeCycleName)
end

return true
end

Expand All @@ -1501,7 +1502,7 @@ end
function RoQuestServer:_LoadPlayerData(player: Player): ()
self._Quests[player] = {} -- Reset our player quest
self._Troves[player]:Clean()

for _questStatus, questArray: {[string]: QuestProgress} in self:GetPlayerData(player) do
for questId: string, questProgress: QuestProgress in questArray do
self:_GiveQuest(player, questId, questProgress)
Expand Down Expand Up @@ -1555,6 +1556,7 @@ function RoQuestServer:_NewPlayerAvailableQuest(player: Player, questId: string)
end

if quest.QuestAcceptType == QuestAcceptType.Automatic then
self.OnQuestAvailable:Fire(player, questId)
self:GiveQuest(player, questId)
elseif not self._AvailableQuests[player][questId] then
self._AvailableQuests[player][questId] = true
Expand Down Expand Up @@ -1608,10 +1610,10 @@ function RoQuestServer:_CreateLifeCycle(player: Player, quest: Quest, lifeCycleN

self._LifeCycles[player][lifeCycleName][quest.QuestId] = newLifeCycle

local startStatus: QuestStatus = quest:GetQuestStatus()
self:_CallLifeCycle(player, quest.QuestId, lifeCycleName, "OnInit")

if StatusToLifeCycle[quest:GetQuestStatus()] then
self:_CallLifeCycle(player, quest.QuestId, lifeCycleName, StatusToLifeCycle[quest:GetQuestStatus()])
if StatusToLifeCycle[startStatus] then
self:_CallLifeCycle(player, quest.QuestId, lifeCycleName, StatusToLifeCycle[startStatus])
end
end

Expand Down
14 changes: 12 additions & 2 deletions RoQuest/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,17 @@ else
Client = nil
end

return {
return setmetatable({
Client = Client,
Server = Server,
}
}, {
__index = function(_, key)
if key == "Server" then
error("Cannot access RoQuest.Server on the client! Make sure to require(RoQuest).Client instead!")
elseif key == "Client" then
error("Cannot access RoQuest.Client on the server! Make sure to require(RoQuest).Server instead!")
else
error("RoQuest has no member named '" .. tostring(key) .. "'")
end
end
})
10 changes: 1 addition & 9 deletions docs/DataStore.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,21 +52,13 @@ RoQuest.OnStart():andThen(function()
end

Players.PlayerRemoving:Connect(playerRemoved)

RoQuest.OnQuestObjectiveChanged:Connect(function(player: Player, questId: string, objectiveId: string, newValue: number)
print(player.Name, " got ", newValue, objectiveId)
end)

RoQuest.OnQuestCompleted:Connect(function(player: Player, questId: string)
print(player.Name, " just completed the quest: ", questId)
end)
end)
```

## 🔍 ProfileService

```lua
local ProfileStore = ProfileService.GetProfileStore("RamdomKey", {})
local ProfileStore = ProfileService.GetProfileStore("RandomKey", {})
local profile = ProfileStore:LoadProfileAsync("USER_"..userId)

if not profile.Data then
Expand Down
106 changes: 106 additions & 0 deletions docs/LifeCycles/CreatingLifeCycle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
---
sidebar_position: 2
sidebar_label: "♻️ Creating LifeCycle"
---

# ♻️ Creating LifeCycle

## 🪜 Create Stairs

Now that we have a base understanding of a lifecycle let's take a look at it in action!

For this example we'll make an object that is too high for the player to collect! However whenever the player accepts the quest stairs spawn in! These stairs will allow the player to climb up and get the object!

Remember the apple quest from the earlier sections? We'll be using that for this one too! Let's start by creating a platform and some stairs...

![](StairsAndHill.png)

Make sure to keep the stairs as their own model!

## 📂 Create Folder Structure

For me this is one of the most important steps to take into account! We want to make sure that we have a good place to store our lifeCycles. The following example is my personal recommendation but feel free to store lifecycles as you please!

![](LifeCyclesStructure.png)

## 💖 Create LifeCycle

Now let's make the lifecycle! Since we only want to display the stairs for 1 player we'll create this lifecycle on the client folder! Here's an example:

```lua
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local RoQuest = require(ReplicatedStorage.RoQuest).Client

local QuestLifeCycle = RoQuest.QuestLifeCycle {
Name = "AppleLifeCycle" -- Important unique identifier
}

function QuestLifeCycle:OnInit() -- Called when the player joins
self.stairs = workspace.Stairs
self.stairs.Parent = ReplicatedStorage
end

function QuestLifeCycle:OnStart()
self.stairs.Parent = workspace
end

function QuestLifeCycle:OnDeliver()
self.stairs.Parent = ReplicatedStorage
end

return QuestLifeCycle
```

## 🔧 Setup Lifecycle

Now that we created our lifecycle we need to connect it to the quest we wish

```lua
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local RoQuest = require(ReplicatedStorage.RoQuest).Server
local appleObjective = require(ReplicatedStorage.QuestObjectives.AppleInfo)

local Quest = RoQuest.Quest

return Quest {
Name = "Collect Apples",
Description = "Collect 2 apples",
QuestId = "AppleCollection",
LifeCycles = {"AppleQuest"}, -- The lifecycles that will manage this quest's behavior
QuestObjectives = {
appleObjective:NewObjective(2)
},
}
```

## ⌛ Load LifeCycles

:::info

Please remember that lifecycles need to be loaded in **both** the client and server

:::

```lua
-- Server
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")

local RoQuest = require(ReplicatedStorage.RoQuest).Server

local questsStore = DataStoreService:GetDataStore("PlayerQuests")

RoQuest:Init(RoQuest:LoadDirectory(ReplicatedStorage.Quests), RoQuest:LoadDirectory(ReplicatedStorage.LifeCycles.Server))
```

```lua
-- Client
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local RoQuest = require(ReplicatedStorage.RoQuest).Client

RoQuest:Init(RoQuest:LoadDirectory(ReplicatedStorage.LifeCycles.Client))
```
4 changes: 0 additions & 4 deletions docs/LifeCycles/CreatingLifeCycles.md

This file was deleted.

4 changes: 0 additions & 4 deletions docs/LifeCycles/Example.md

This file was deleted.

Binary file added docs/LifeCycles/LifeCyclesStructure.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/LifeCycles/QuestLifecycle.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/LifeCycles/StairsAndHill.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions docs/LifeCycles/WhatIsLifeCycle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
sidebar_position: 1
sidebar_label: "🤔 What is a lifecycle?"
---

# 🤔 What is a lifecycle?

LifeCycles are a feature that I've been a huge fan ever since I started developing libraries. Although it is something simple, it is also something incredibly powerful to create fragmented reusable code.

But before we jump into making our own lifecycle first we need to understand what it is. Imagine a lifecycle as the behavior of a quest. With this you can determine how a quest behaves when you start it, progress on it, complete it, cancel it, deliver it and so on! In short you can see the Quest as the data and the lifecycle as the behavior of said data!

![](QuestLifecycle.png)

:::info
If a player rejoins the game and reloads his data, it will **only** call the lifecycle part where the player was left on.
So if the player already completed the quest but didn't deliver it, when he rejoins it will only call the OnComplete function
:::

## Client Vs Server

LifeCycles can both run on the server and or client. This all depends on how the developer injects the lifeCycles into their RoQuest! I'll be showing some examples on the following section for lifeCycles.
Loading

0 comments on commit 13466ba

Please sign in to comment.