@quenty/rogue-properties 1.0.1-canary.256.8cd6f5a.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/LICENSE.md +21 -0
- package/README.md +35 -0
- package/default.project.json +6 -0
- package/package.json +47 -0
- package/src/Shared/Definition/RoguePropertyDefinition.lua +97 -0
- package/src/Shared/Definition/RoguePropertyTableDefinition.lua +139 -0
- package/src/Shared/Modifiers/Additive/RogueAdditive.lua +34 -0
- package/src/Shared/Modifiers/Additive/RogueAdditiveProvider.lua +91 -0
- package/src/Shared/Modifiers/Multiplier/RogueMultiplier.lua +34 -0
- package/src/Shared/Modifiers/Multiplier/RogueMultiplierProvider.lua +91 -0
- package/src/Shared/Modifiers/RoguePropertyModifierConstants.lua +11 -0
- package/src/Shared/Modifiers/RoguePropertyModifierUtils.lua +21 -0
- package/src/Shared/Property/RogueProperty.lua +219 -0
- package/src/Shared/Property/RoguePropertyUtils.lua +39 -0
- package/src/Shared/RogueBindersShared.lua +13 -0
- package/src/Shared/RoguePropertyBinderGroups.lua +17 -0
- package/src/Shared/RoguePropertyService.lua +69 -0
- package/src/Shared/Table/RoguePropertyTable.lua +87 -0
- package/src/node_modules.project.json +7 -0
- package/test/default.project.json +21 -0
- package/test/scripts/Client/ClientMain.client.lua +11 -0
- package/test/scripts/Server/ServerMain.server.lua +41 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
|
+
|
|
6
|
+
## 1.0.1-canary.256.8cd6f5a.0 (2022-03-27)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* Add RogueProperty package ([87114a1](https://github.com/Quenty/NevermoreEngine/commit/87114a11e33015e4eccf7d907ac2fce2e2d889c0))
|
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2014-2021 Quenty
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
## RogueProperties
|
|
2
|
+
<div align="center">
|
|
3
|
+
<a href="http://quenty.github.io/NevermoreEngine/">
|
|
4
|
+
<img src="https://github.com/Quenty/NevermoreEngine/actions/workflows/docs.yml/badge.svg" alt="Documentation status" />
|
|
5
|
+
</a>
|
|
6
|
+
<a href="https://discord.gg/mhtGUS8">
|
|
7
|
+
<img src="https://img.shields.io/discord/385151591524597761?color=5865F2&label=discord&logo=discord&logoColor=white" alt="Discord" />
|
|
8
|
+
</a>
|
|
9
|
+
<a href="https://github.com/Quenty/NevermoreEngine/actions">
|
|
10
|
+
<img src="https://github.com/Quenty/NevermoreEngine/actions/workflows/build.yml/badge.svg" alt="Build and release status" />
|
|
11
|
+
</a>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<div align="center"><a href="https://quenty.github.io/NevermoreEngine/api/RogueProperty">View docs →</a></div>
|
|
15
|
+
|
|
16
|
+
Roguelike properties which can be modified by external provides -- and that modification can be attributed to a source.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
```
|
|
20
|
+
npm install @quenty/rogueproperties --save
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Design goals
|
|
24
|
+
We need a property system for a rogue-like or MMORPG style game that offers the following attributes.
|
|
25
|
+
|
|
26
|
+
1. Modifiable - Can modify a base property in a variety of ways (additive, multiplicative, et cetera)
|
|
27
|
+
2. Attributable - Can attribute the source of final computation, especially for UI.
|
|
28
|
+
3. Extensible - Can combine properties
|
|
29
|
+
4. Grounded in Roblox datamodel - Source of truth exists in Roblox so other people can modify it
|
|
30
|
+
5. Performant - Needs to run fast
|
|
31
|
+
6. Agnostic to server/client - Needs to be able to centralize in datatable.
|
|
32
|
+
7. Scriptable - Need to be able to apply weird scripts to data.
|
|
33
|
+
8. Levelable - Can scale with level!
|
|
34
|
+
9. Usable for other people - The computed value is just .Value under the object!
|
|
35
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@quenty/rogue-properties",
|
|
3
|
+
"version": "1.0.1-canary.256.8cd6f5a.0",
|
|
4
|
+
"description": "Roguelike properties which can be modified by external provides",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"Roblox",
|
|
7
|
+
"Nevermore",
|
|
8
|
+
"Lua"
|
|
9
|
+
],
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/Quenty/NevermoreEngine/issues"
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/Quenty/NevermoreEngine.git",
|
|
16
|
+
"directory": "src/rogue-properties/"
|
|
17
|
+
},
|
|
18
|
+
"funding": {
|
|
19
|
+
"type": "patreon",
|
|
20
|
+
"url": "https://www.patreon.com/quenty"
|
|
21
|
+
},
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"contributors": [
|
|
24
|
+
"Quenty"
|
|
25
|
+
],
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@quenty/baseobject": "4.1.1-canary.256.8cd6f5a.0",
|
|
28
|
+
"@quenty/binder": "5.1.1-canary.256.8cd6f5a.0",
|
|
29
|
+
"@quenty/brio": "5.1.1-canary.256.8cd6f5a.0",
|
|
30
|
+
"@quenty/instanceutils": "4.1.1-canary.256.8cd6f5a.0",
|
|
31
|
+
"@quenty/jsonutils": "4.1.1-canary.256.8cd6f5a.0",
|
|
32
|
+
"@quenty/linkutils": "4.1.1-canary.256.8cd6f5a.0",
|
|
33
|
+
"@quenty/loader": "4.0.1-canary.256.8cd6f5a.0",
|
|
34
|
+
"@quenty/maid": "2.2.1-canary.256.8cd6f5a.0",
|
|
35
|
+
"@quenty/rx": "4.1.1-canary.256.8cd6f5a.0",
|
|
36
|
+
"@quenty/rxbinderutils": "5.1.1-canary.256.8cd6f5a.0",
|
|
37
|
+
"@quenty/servicebag": "4.1.1-canary.256.8cd6f5a.0",
|
|
38
|
+
"@quenty/signal": "2.1.1-canary.256.8cd6f5a.0",
|
|
39
|
+
"@quenty/table": "2.1.2-canary.256.8cd6f5a.0",
|
|
40
|
+
"@quenty/valuebaseutils": "4.1.1-canary.256.8cd6f5a.0",
|
|
41
|
+
"@quentystudios/t": "^1.2.5"
|
|
42
|
+
},
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
},
|
|
46
|
+
"gitHead": "8cd6f5a870e3e400eb53d147873062111e6ae8ed"
|
|
47
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
--[=[
|
|
2
|
+
@class RoguePropertyDefinition
|
|
3
|
+
]=]
|
|
4
|
+
|
|
5
|
+
local require = require(script.Parent.loader).load(script)
|
|
6
|
+
|
|
7
|
+
local BaseObject = require("BaseObject")
|
|
8
|
+
local RogueProperty = require("RogueProperty")
|
|
9
|
+
local ServiceBag = require("ServiceBag")
|
|
10
|
+
local RoguePropertyUtils = require("RoguePropertyUtils")
|
|
11
|
+
|
|
12
|
+
local RoguePropertyDefinition = setmetatable({}, BaseObject)
|
|
13
|
+
RoguePropertyDefinition.ClassName = "RoguePropertyDefinition"
|
|
14
|
+
RoguePropertyDefinition.__index = RoguePropertyDefinition
|
|
15
|
+
|
|
16
|
+
function RoguePropertyDefinition.new(name, defaultValue, roguePropertyTableDefinition)
|
|
17
|
+
local self = setmetatable(BaseObject.new(), RoguePropertyDefinition)
|
|
18
|
+
|
|
19
|
+
assert(defaultValue ~= nil, "Bad defaultValue")
|
|
20
|
+
|
|
21
|
+
self._name = assert(name, "Bad name")
|
|
22
|
+
self._defaultValue = defaultValue
|
|
23
|
+
self._valueType = typeof(self._defaultValue)
|
|
24
|
+
self._storageType = self:_computeStorageInstanceType()
|
|
25
|
+
self._roguePropertyTableDefinition = roguePropertyTableDefinition or nil
|
|
26
|
+
self._encodedDefaultValue = RoguePropertyUtils.encodeProperty(self, self._defaultValue)
|
|
27
|
+
|
|
28
|
+
return self
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
--[=[
|
|
32
|
+
@param serviceBag ServiceBag
|
|
33
|
+
@param adornee Instance
|
|
34
|
+
@return RogueProperty
|
|
35
|
+
]=]
|
|
36
|
+
function RoguePropertyDefinition:Get(serviceBag, adornee)
|
|
37
|
+
assert(ServiceBag.isServiceBag(serviceBag), "Bad serviceBag")
|
|
38
|
+
assert(typeof(adornee) == "Instance", "Bad adornee")
|
|
39
|
+
|
|
40
|
+
return RogueProperty.new(adornee, serviceBag, self)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
function RoguePropertyDefinition:GetPropertyTableDefinition()
|
|
45
|
+
return self._roguePropertyTableDefinition
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
--[=[
|
|
49
|
+
Gets the name of the rogue property
|
|
50
|
+
@return string
|
|
51
|
+
]=]
|
|
52
|
+
function RoguePropertyDefinition:GetName(): string
|
|
53
|
+
return self._name
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
--[=[
|
|
57
|
+
Gets the default value for the property
|
|
58
|
+
@return TProperty
|
|
59
|
+
]=]
|
|
60
|
+
function RoguePropertyDefinition:GetDefaultValue()
|
|
61
|
+
return self._defaultValue
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
function RoguePropertyDefinition:GetValueType()
|
|
65
|
+
return self._valueType
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
function RoguePropertyDefinition:GetStorageInstanceType()
|
|
69
|
+
return self._storageType
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
function RoguePropertyDefinition:GetEncodedDefaultValue()
|
|
73
|
+
return rawget(self, "_encodedDefaultValue")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
function RoguePropertyDefinition:_computeStorageInstanceType()
|
|
77
|
+
if self._valueType == "string" or self._valueType == "table" then
|
|
78
|
+
return "StringValue"
|
|
79
|
+
elseif self._valueType == "number" then
|
|
80
|
+
return "NumberValue"
|
|
81
|
+
elseif self._valueType == "boolean" then
|
|
82
|
+
return "BoolValue"
|
|
83
|
+
elseif self._valueType == "Color3" then
|
|
84
|
+
return "Color3Value"
|
|
85
|
+
elseif self._valueType == "BrickColor" then
|
|
86
|
+
return "BrickColorValue"
|
|
87
|
+
elseif self._valueType == "Vector3" then
|
|
88
|
+
return "Vector3Value"
|
|
89
|
+
elseif self._valueType == "CFrame" then
|
|
90
|
+
return "CFrameValue"
|
|
91
|
+
else
|
|
92
|
+
error(("Unknown valueType %q"):format(tostring(self._valueType)))
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
return RoguePropertyDefinition
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
--[=[
|
|
2
|
+
This holds the definition for a variety of tables.
|
|
3
|
+
@class RoguePropertyTableDefinition
|
|
4
|
+
]=]
|
|
5
|
+
|
|
6
|
+
local require = require(script.Parent.loader).load(script)
|
|
7
|
+
|
|
8
|
+
local RoguePropertyTable = require("RoguePropertyTable")
|
|
9
|
+
local ServiceBag = require("ServiceBag")
|
|
10
|
+
local RoguePropertyDefinition = require("RoguePropertyDefinition")
|
|
11
|
+
local RxInstanceUtils = require("RxInstanceUtils")
|
|
12
|
+
local RoguePropertyService = require("RoguePropertyService")
|
|
13
|
+
|
|
14
|
+
local RoguePropertyTableDefinition = {}
|
|
15
|
+
RoguePropertyTableDefinition.ClassName = "RoguePropertyTableDefinition"
|
|
16
|
+
RoguePropertyTableDefinition.__index = RoguePropertyTableDefinition
|
|
17
|
+
|
|
18
|
+
function RoguePropertyTableDefinition.new(tableName: string, propertyDefinition: {[string]: any})
|
|
19
|
+
local self = setmetatable({}, RoguePropertyTableDefinition)
|
|
20
|
+
|
|
21
|
+
assert(type(tableName) == "string", "Bad tableName")
|
|
22
|
+
assert(type(propertyDefinition) == "table", "Bad propertyDefinition")
|
|
23
|
+
|
|
24
|
+
self._tableName = tableName
|
|
25
|
+
self._definitionMap = {}
|
|
26
|
+
|
|
27
|
+
for key, defaultValue in pairs(propertyDefinition) do
|
|
28
|
+
self._definitionMap[key] = RoguePropertyDefinition.new(key, defaultValue, self)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
return self
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
--[=[
|
|
35
|
+
Gets the name of the rogue property table
|
|
36
|
+
@return string
|
|
37
|
+
]=]
|
|
38
|
+
function RoguePropertyTableDefinition:GetName(): string
|
|
39
|
+
return self._tableName
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
function RoguePropertyTableDefinition:GetDefinitionMap()
|
|
43
|
+
return self._definitionMap
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
--[=[
|
|
47
|
+
Gets the RoguePropertyDefinition if it exists
|
|
48
|
+
@param propertyName
|
|
49
|
+
@return RoguePropertyDefinition?
|
|
50
|
+
]=]
|
|
51
|
+
function RoguePropertyTableDefinition:GetDefinition(propertyName: string)
|
|
52
|
+
assert(type(propertyName) == "string", "Bad propertyName")
|
|
53
|
+
|
|
54
|
+
local definitions = rawget(self, "_definitionMap")
|
|
55
|
+
return definitions[propertyName]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
--[=[
|
|
59
|
+
Gets a new property table for a given object, which can compute the modified
|
|
60
|
+
value of the adornee. This will initialize the properties on the server.
|
|
61
|
+
|
|
62
|
+
@param serviceBag ServiceBag
|
|
63
|
+
@param adornee Instance
|
|
64
|
+
@return RoguePropertyTable
|
|
65
|
+
]=]
|
|
66
|
+
function RoguePropertyTableDefinition:GetPropertyTable(serviceBag, adornee)
|
|
67
|
+
assert(ServiceBag.isServiceBag(serviceBag), "Bad serviceBag")
|
|
68
|
+
assert(typeof(adornee) == "Instance", "Bad adornee")
|
|
69
|
+
|
|
70
|
+
return RoguePropertyTable.new(adornee, serviceBag, self)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
--[=[
|
|
74
|
+
Observes the current container while it exists for the given adornee.
|
|
75
|
+
|
|
76
|
+
@param serviceBag ServiceBag
|
|
77
|
+
@param adornee Instance
|
|
78
|
+
@return Observable<Brio<Folder>>
|
|
79
|
+
]=]
|
|
80
|
+
function RoguePropertyTableDefinition:ObserveContainerBrio(serviceBag, adornee)
|
|
81
|
+
assert(ServiceBag.isServiceBag(serviceBag), "Bad serviceBag")
|
|
82
|
+
assert(typeof(adornee) == "Instance", "Bad adornee")
|
|
83
|
+
|
|
84
|
+
-- TODO: Optimize so we aren't duplcating this logic each time we index a property
|
|
85
|
+
if serviceBag:GetService(RoguePropertyService):CanInitializeProperties() then
|
|
86
|
+
self:_ensureContainer(adornee)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
return RxInstanceUtils.observeLastNamedChildBrio(adornee, "Folder", self._tableName)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
--[=[
|
|
93
|
+
Gets the current container for the given adornee.
|
|
94
|
+
@param serviceBag ServiceBag
|
|
95
|
+
@param adornee Instance
|
|
96
|
+
@return Folder?
|
|
97
|
+
]=]
|
|
98
|
+
function RoguePropertyTableDefinition:GetContainer(serviceBag, adornee)
|
|
99
|
+
assert(ServiceBag.isServiceBag(serviceBag), "Bad serviceBag")
|
|
100
|
+
assert(typeof(adornee) == "Instance", "Bad adornee")
|
|
101
|
+
|
|
102
|
+
if serviceBag:GetService(RoguePropertyService):CanInitializeProperties() then
|
|
103
|
+
return self:_ensureContainer(adornee)
|
|
104
|
+
else
|
|
105
|
+
return adornee:FindFirstChild(self._tableName)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
function RoguePropertyTableDefinition:_ensureContainer(adornee)
|
|
110
|
+
local existing = adornee:FindFirstChild(self._tableName)
|
|
111
|
+
if existing then
|
|
112
|
+
return existing
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
local folder = Instance.new("Folder")
|
|
116
|
+
folder.Name = self._tableName
|
|
117
|
+
folder.Parent = adornee
|
|
118
|
+
return folder
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
function RoguePropertyTableDefinition:__index(index)
|
|
122
|
+
assert(type(index) == "string", "Bad index")
|
|
123
|
+
|
|
124
|
+
if index == "_definitionMap" then
|
|
125
|
+
return rawget(self, "_definitionMap")
|
|
126
|
+
elseif RoguePropertyTableDefinition[index] then
|
|
127
|
+
return RoguePropertyTableDefinition[index]
|
|
128
|
+
else
|
|
129
|
+
local definitions = rawget(self, "_definitionMap")
|
|
130
|
+
|
|
131
|
+
if definitions[index] then
|
|
132
|
+
return definitions[index]
|
|
133
|
+
else
|
|
134
|
+
error(("Bad definition %q"):format(tostring(index)))
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
return RoguePropertyTableDefinition
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
--[=[
|
|
2
|
+
@class RogueAdditive
|
|
3
|
+
]=]
|
|
4
|
+
|
|
5
|
+
local require = require(script.Parent.loader).load(script)
|
|
6
|
+
|
|
7
|
+
local BaseObject = require("BaseObject")
|
|
8
|
+
local RxValueBaseUtils = require("RxValueBaseUtils")
|
|
9
|
+
|
|
10
|
+
local RogueAdditive = setmetatable({}, BaseObject)
|
|
11
|
+
RogueAdditive.ClassName = "RogueAdditive"
|
|
12
|
+
RogueAdditive.__index = RogueAdditive
|
|
13
|
+
|
|
14
|
+
function RogueAdditive.new(obj, serviceBag)
|
|
15
|
+
local self = setmetatable(BaseObject.new(obj), RogueAdditive)
|
|
16
|
+
|
|
17
|
+
self._serviceBag = assert(serviceBag, "No serviceBag")
|
|
18
|
+
|
|
19
|
+
return self
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
function RogueAdditive:GetObject()
|
|
23
|
+
return self._obj
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
function RogueAdditive:ObserveAdditive()
|
|
27
|
+
return RxValueBaseUtils.observeValue(self._obj)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
function RogueAdditive:GetAdditive()
|
|
31
|
+
return self._obj.Value
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
return RogueAdditive
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
--[=[
|
|
2
|
+
@class RogueAdditiveProvider
|
|
3
|
+
]=]
|
|
4
|
+
|
|
5
|
+
local require = require(script.Parent.loader).load(script)
|
|
6
|
+
|
|
7
|
+
local Rx = require("Rx")
|
|
8
|
+
local RxBinderUtils = require("RxBinderUtils")
|
|
9
|
+
local RxBrioUtils = require("RxBrioUtils")
|
|
10
|
+
local RogueBindersShared = require("RogueBindersShared")
|
|
11
|
+
local BinderUtils = require("BinderUtils")
|
|
12
|
+
local RoguePropertyModifierUtils = require("RoguePropertyModifierUtils")
|
|
13
|
+
local RoguePropertyService = require("RoguePropertyService")
|
|
14
|
+
|
|
15
|
+
local RogueAdditiveProvider = {}
|
|
16
|
+
|
|
17
|
+
function RogueAdditiveProvider:Init(serviceBag)
|
|
18
|
+
assert(not self._serviceBag, "Already initialized")
|
|
19
|
+
self._serviceBag = assert(serviceBag, "No serviceBag")
|
|
20
|
+
|
|
21
|
+
-- Internal
|
|
22
|
+
self._roguePropetyService = self._serviceBag:GetService(RoguePropertyService)
|
|
23
|
+
self._rogueBinders = self._serviceBag:GetService(RogueBindersShared)
|
|
24
|
+
|
|
25
|
+
self._roguePropetyService:AddProvider(self)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
function RogueAdditiveProvider:GetBinder()
|
|
29
|
+
return self._rogueBinders.RogueAdditive
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
function RogueAdditiveProvider:Create(additive, source)
|
|
33
|
+
assert(type(additive) == "number", "Bad additive")
|
|
34
|
+
assert(typeof(source) == "Instance" or source == nil, "Bad source")
|
|
35
|
+
|
|
36
|
+
local obj = Instance.new("NumberValue")
|
|
37
|
+
obj.Name = "Additive"
|
|
38
|
+
obj.Value = additive
|
|
39
|
+
|
|
40
|
+
if source then
|
|
41
|
+
RoguePropertyModifierUtils.createSourceLink(obj, source)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
self._rogueBinders.RogueAdditive:Bind(obj)
|
|
45
|
+
|
|
46
|
+
return obj
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
function RogueAdditiveProvider:GetModifiedVersion(propObj, rogueProperty, baseValue)
|
|
50
|
+
if rogueProperty:GetDefinition():GetValueType() == "number" then
|
|
51
|
+
local value = baseValue
|
|
52
|
+
|
|
53
|
+
for _, item in pairs(self:_getAdditives(propObj)) do
|
|
54
|
+
value = value + item:GetAdditive()
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
return value
|
|
58
|
+
else
|
|
59
|
+
return baseValue
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
function RogueAdditiveProvider:ObserveModifiedVersion(propObj, rogueProperty, observeBaseValue)
|
|
64
|
+
if rogueProperty:GetDefinition():GetValueType() == "number" then
|
|
65
|
+
return RxBrioUtils.flatCombineLatest({
|
|
66
|
+
value = observeBaseValue;
|
|
67
|
+
multiplier = self:_observeMultipliers(propObj):Pipe({
|
|
68
|
+
RxBrioUtils.flatMapBrio(function(item)
|
|
69
|
+
return item:ObserveAdditive();
|
|
70
|
+
end); -- this gets us a list of multipliers which should mutate pretty frequently.
|
|
71
|
+
Rx.defaultsToNil;
|
|
72
|
+
});
|
|
73
|
+
}):Pipe({
|
|
74
|
+
Rx.map(function(state)
|
|
75
|
+
return self:GetModifiedVersion(propObj, rogueProperty, state.value)
|
|
76
|
+
end);
|
|
77
|
+
})
|
|
78
|
+
else
|
|
79
|
+
return observeBaseValue
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
function RogueAdditiveProvider:_observeMultipliers(propObj)
|
|
84
|
+
return RxBinderUtils.observeBoundChildClassBrio(self._rogueBinders.RogueAdditive, propObj)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
function RogueAdditiveProvider:_getAdditives(propObj)
|
|
88
|
+
return BinderUtils.getChildren(self._rogueBinders.RogueAdditive, propObj)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
return RogueAdditiveProvider
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
--[=[
|
|
2
|
+
@class RogueMultiplier
|
|
3
|
+
]=]
|
|
4
|
+
|
|
5
|
+
local require = require(script.Parent.loader).load(script)
|
|
6
|
+
|
|
7
|
+
local BaseObject = require("BaseObject")
|
|
8
|
+
local RxValueBaseUtils = require("RxValueBaseUtils")
|
|
9
|
+
|
|
10
|
+
local RogueMultiplier = setmetatable({}, BaseObject)
|
|
11
|
+
RogueMultiplier.ClassName = "RogueMultiplier"
|
|
12
|
+
RogueMultiplier.__index = RogueMultiplier
|
|
13
|
+
|
|
14
|
+
function RogueMultiplier.new(obj, serviceBag)
|
|
15
|
+
local self = setmetatable(BaseObject.new(obj), RogueMultiplier)
|
|
16
|
+
|
|
17
|
+
self._serviceBag = assert(serviceBag, "No serviceBag")
|
|
18
|
+
|
|
19
|
+
return self
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
function RogueMultiplier:ObserveMultiplier()
|
|
23
|
+
return RxValueBaseUtils.observeValue(self._obj)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
function RogueMultiplier:GetMultiplier()
|
|
27
|
+
return self._obj.Value
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
function RogueMultiplier:GetObject()
|
|
31
|
+
return self._obj
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
return RogueMultiplier
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
--[=[
|
|
2
|
+
@class RogueMultiplierProvider
|
|
3
|
+
]=]
|
|
4
|
+
|
|
5
|
+
local require = require(script.Parent.loader).load(script)
|
|
6
|
+
|
|
7
|
+
local Rx = require("Rx")
|
|
8
|
+
local RxBinderUtils = require("RxBinderUtils")
|
|
9
|
+
local RxBrioUtils = require("RxBrioUtils")
|
|
10
|
+
local RogueBindersShared = require("RogueBindersShared")
|
|
11
|
+
local BinderUtils = require("BinderUtils")
|
|
12
|
+
local RoguePropertyModifierUtils = require("RoguePropertyModifierUtils")
|
|
13
|
+
local RoguePropertyService = require("RoguePropertyService")
|
|
14
|
+
|
|
15
|
+
local RogueMultiplierProvider = {}
|
|
16
|
+
|
|
17
|
+
function RogueMultiplierProvider:Init(serviceBag)
|
|
18
|
+
assert(not self._serviceBag, "Already initialized")
|
|
19
|
+
self._serviceBag = assert(serviceBag, "No serviceBag")
|
|
20
|
+
|
|
21
|
+
-- Internal
|
|
22
|
+
self._roguePropetyService = self._serviceBag:GetService(RoguePropertyService)
|
|
23
|
+
self._rogueBinders = self._serviceBag:GetService(RogueBindersShared)
|
|
24
|
+
|
|
25
|
+
self._roguePropetyService:AddProvider(self)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
function RogueMultiplierProvider:GetBinder()
|
|
29
|
+
return self._rogueBinders.RogueMultiplier
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
function RogueMultiplierProvider:Create(multiplier, source)
|
|
33
|
+
assert(type(multiplier) == "number", "Bad multiplier")
|
|
34
|
+
assert(typeof(source) == "Instance" or source == nil, "Bad source")
|
|
35
|
+
|
|
36
|
+
local obj = Instance.new("NumberValue")
|
|
37
|
+
obj.Name = "Multiplier"
|
|
38
|
+
obj.Value = multiplier
|
|
39
|
+
|
|
40
|
+
if source then
|
|
41
|
+
RoguePropertyModifierUtils.createSourceLink(obj, source)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
self._rogueBinders.RogueMultiplier:Bind(obj)
|
|
45
|
+
|
|
46
|
+
return obj
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
function RogueMultiplierProvider:GetModifiedVersion(propObj, rogueProperty, baseValue)
|
|
50
|
+
if rogueProperty:GetDefinition():GetValueType() == "number" then
|
|
51
|
+
local multiplier = 1
|
|
52
|
+
|
|
53
|
+
for _, item in pairs(self:_getMultipliers(propObj)) do
|
|
54
|
+
multiplier = multiplier*item:GetMultiplier()
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
return baseValue*multiplier
|
|
58
|
+
else
|
|
59
|
+
return baseValue
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
function RogueMultiplierProvider:ObserveModifiedVersion(propObj, rogueProperty, observeBaseValue)
|
|
64
|
+
if rogueProperty:GetDefinition():GetValueType() == "number" then
|
|
65
|
+
return RxBrioUtils.flatCombineLatest({
|
|
66
|
+
value = observeBaseValue;
|
|
67
|
+
multiplier = self:_observeMultipliers(propObj):Pipe({
|
|
68
|
+
RxBrioUtils.flatMapBrio(function(item)
|
|
69
|
+
return item:ObserveMultiplier();
|
|
70
|
+
end); -- this gets us a list of multipliers which should mutate pretty frequently.
|
|
71
|
+
Rx.defaultsToNil;
|
|
72
|
+
});
|
|
73
|
+
}):Pipe({
|
|
74
|
+
Rx.map(function(state)
|
|
75
|
+
return self:GetModifiedVersion(propObj, rogueProperty, state.value)
|
|
76
|
+
end);
|
|
77
|
+
})
|
|
78
|
+
else
|
|
79
|
+
return observeBaseValue
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
function RogueMultiplierProvider:_observeMultipliers(propObj)
|
|
84
|
+
return RxBinderUtils.observeBoundChildClassBrio(self._rogueBinders.RogueMultiplier, propObj)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
function RogueMultiplierProvider:_getMultipliers(propObj)
|
|
88
|
+
return BinderUtils.getChildren(self._rogueBinders.RogueMultiplier, propObj)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
return RogueMultiplierProvider
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
--[=[
|
|
2
|
+
@class RoguePropertyModifierUtils
|
|
3
|
+
]=]
|
|
4
|
+
|
|
5
|
+
local require = require(script.Parent.loader).load(script)
|
|
6
|
+
|
|
7
|
+
local LinkUtils = require("LinkUtils")
|
|
8
|
+
local RoguePropertyModifierConstants = require("RoguePropertyModifierConstants")
|
|
9
|
+
local RxLinkUtils = require("RxLinkUtils")
|
|
10
|
+
|
|
11
|
+
local RoguePropertyModifierUtils = {}
|
|
12
|
+
|
|
13
|
+
function RoguePropertyModifierUtils.createSourceLink(modifier, source)
|
|
14
|
+
return LinkUtils.createLink(RoguePropertyModifierConstants.PROPERTY_SOURCE_LINK_NAME, modifier, source)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
function RoguePropertyModifierUtils.observeSourceLinksBrio(modifier)
|
|
18
|
+
return RxLinkUtils.observeValidLinksBrio(RoguePropertyModifierConstants.PROPERTY_SOURCE_LINK_NAME, modifier)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
return RoguePropertyModifierUtils
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
--[=[
|
|
2
|
+
@class RogueProperty
|
|
3
|
+
]=]
|
|
4
|
+
|
|
5
|
+
local require = require(script.Parent.loader).load(script)
|
|
6
|
+
|
|
7
|
+
local RogueAdditiveProvider = require("RogueAdditiveProvider")
|
|
8
|
+
local RogueMultiplierProvider = require("RogueMultiplierProvider")
|
|
9
|
+
local RoguePropertyBinderGroups = require("RoguePropertyBinderGroups")
|
|
10
|
+
local RoguePropertyModifierUtils = require("RoguePropertyModifierUtils")
|
|
11
|
+
local RoguePropertyService = require("RoguePropertyService")
|
|
12
|
+
local Rx = require("Rx")
|
|
13
|
+
local RxBinderUtils = require("RxBinderUtils")
|
|
14
|
+
local RxBrioUtils = require("RxBrioUtils")
|
|
15
|
+
local RxInstanceUtils = require("RxInstanceUtils")
|
|
16
|
+
local RxValueBaseUtils = require("RxValueBaseUtils")
|
|
17
|
+
local ValueBaseUtils = require("ValueBaseUtils")
|
|
18
|
+
local RoguePropertyUtils = require("RoguePropertyUtils")
|
|
19
|
+
|
|
20
|
+
local RogueProperty = {}
|
|
21
|
+
RogueProperty.ClassName = "RogueProperty"
|
|
22
|
+
RogueProperty.__index = RogueProperty
|
|
23
|
+
|
|
24
|
+
function RogueProperty.new(adornee, serviceBag, definition)
|
|
25
|
+
local self = setmetatable({}, RogueProperty)
|
|
26
|
+
|
|
27
|
+
self._serviceBag = assert(serviceBag, "No serviceBag")
|
|
28
|
+
self._roguePropertyBinderGroups = self._serviceBag:GetService(RoguePropertyBinderGroups)
|
|
29
|
+
self._roguePropertyService = self._serviceBag:GetService(RoguePropertyService)
|
|
30
|
+
|
|
31
|
+
self._adornee = assert(adornee, "Bad adornee")
|
|
32
|
+
self._definition = assert(definition, "Bad definition")
|
|
33
|
+
|
|
34
|
+
if self._roguePropertyService:CanInitializeProperties() then
|
|
35
|
+
self:_getBaseValueObject()
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
return self
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
function RogueProperty:_getBaseValueObject()
|
|
42
|
+
local parent
|
|
43
|
+
local tableDefinition = self._definition:GetPropertyTableDefinition()
|
|
44
|
+
if tableDefinition then
|
|
45
|
+
parent = tableDefinition:GetContainer(self._serviceBag, self._adornee)
|
|
46
|
+
else
|
|
47
|
+
parent = self._adornee
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
if not parent then
|
|
51
|
+
return nil
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
if self._roguePropertyService:CanInitializeProperties() then
|
|
55
|
+
return ValueBaseUtils.getOrCreateValue(
|
|
56
|
+
parent,
|
|
57
|
+
self._definition:GetStorageInstanceType(),
|
|
58
|
+
self._definition:GetName(),
|
|
59
|
+
self._definition:GetEncodedDefaultValue())
|
|
60
|
+
else
|
|
61
|
+
return parent:FindFirstChild(self._definition:GetName())
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
function RogueProperty:_observeBaseValueBrio()
|
|
66
|
+
local tableDefinition = self._definition:GetPropertyTableDefinition()
|
|
67
|
+
if tableDefinition then
|
|
68
|
+
return tableDefinition:ObserveContainerBrio(self._serviceBag, self._adornee)
|
|
69
|
+
:Pipe({
|
|
70
|
+
RxBrioUtils.switchMapBrio(function(container)
|
|
71
|
+
return RxInstanceUtils.observeLastNamedChildBrio(
|
|
72
|
+
container,
|
|
73
|
+
self._definition:GetStorageInstanceType(),
|
|
74
|
+
self._definition:GetName())
|
|
75
|
+
end);
|
|
76
|
+
})
|
|
77
|
+
else
|
|
78
|
+
return RxInstanceUtils.observeLastNamedChildBrio(self._adornee, self._definition:GetStorageInstanceType(), self._definition:GetName())
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
function RogueProperty:SetBaseValue(value)
|
|
83
|
+
local baseValue = self:_getBaseValueObject()
|
|
84
|
+
if baseValue then
|
|
85
|
+
baseValue.Value = self:_encodeValue(value)
|
|
86
|
+
else
|
|
87
|
+
warn("Failed to get the baseValue to parent")
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
function RogueProperty:GetValue()
|
|
92
|
+
local propObj = self:_getBaseValueObject()
|
|
93
|
+
if not propObj then
|
|
94
|
+
return self._definition:GetDefaultValue()
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
local current = self:_decodeValue(propObj.Value)
|
|
98
|
+
|
|
99
|
+
for _, item in pairs(self._roguePropertyService:GetProviders()) do
|
|
100
|
+
current = item:GetModifiedVersion(propObj, self, current)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
return current
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
function RogueProperty:GetDefinition()
|
|
107
|
+
return self._definition
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
function RogueProperty:ObserveModifiersBrio()
|
|
112
|
+
return self:_observeBaseValueBrio()
|
|
113
|
+
:Pipe({
|
|
114
|
+
RxBrioUtils.flatMapBrio(function(baseValue)
|
|
115
|
+
if baseValue then
|
|
116
|
+
return RxBinderUtils.observeBoundChildClassesBrio(self._roguePropertyBinderGroups.RogueModifiers:GetBinders(), baseValue)
|
|
117
|
+
else
|
|
118
|
+
return Rx.EMPTY
|
|
119
|
+
end
|
|
120
|
+
end);
|
|
121
|
+
})
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
function RogueProperty:ObserveSourcesBrio()
|
|
125
|
+
return self:ObserveModifiersBrio()
|
|
126
|
+
:Pipe({
|
|
127
|
+
RxBrioUtils.flatMapBrio(function(rogueModifier)
|
|
128
|
+
return RoguePropertyModifierUtils.observeSourceLinksBrio(rogueModifier:GetObject())
|
|
129
|
+
end);
|
|
130
|
+
})
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
function RogueProperty:Observe()
|
|
134
|
+
return self._roguePropertyService:ObserveProviderList():Pipe({
|
|
135
|
+
RxBrioUtils.toBrio();
|
|
136
|
+
RxBrioUtils.switchMapBrio(function()
|
|
137
|
+
return self:_observeBaseValueBrio()
|
|
138
|
+
end);
|
|
139
|
+
RxBrioUtils.switchMapBrio(function(baseValue)
|
|
140
|
+
local current
|
|
141
|
+
if baseValue then
|
|
142
|
+
current = RxValueBaseUtils.observeValue(baseValue)
|
|
143
|
+
|
|
144
|
+
if self._definition:GetValueType() == "table" then
|
|
145
|
+
current = current:Pipe({
|
|
146
|
+
Rx.map(function(value)
|
|
147
|
+
return self:_decodeValue(value)
|
|
148
|
+
end)
|
|
149
|
+
})
|
|
150
|
+
end
|
|
151
|
+
else
|
|
152
|
+
current = Rx.of(self._definition:GetDefaultValue())
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
for _, item in pairs(self._roguePropertyService:GetProviders()) do
|
|
156
|
+
current = item:ObserveModifiedVersion(baseValue, self, current)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
return current
|
|
160
|
+
end);
|
|
161
|
+
RxBrioUtils.emitOnDeath(self._definition:GetDefaultValue());
|
|
162
|
+
})
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
function RogueProperty:CreateMultiplier(amount, source)
|
|
166
|
+
assert(type(amount) == "number", "Bad amount")
|
|
167
|
+
|
|
168
|
+
local provider = self._serviceBag:GetService(RogueMultiplierProvider)
|
|
169
|
+
local baseValue = self:_getBaseValueObject()
|
|
170
|
+
|
|
171
|
+
if not baseValue then
|
|
172
|
+
warn("Failed to get the baseValue to parent")
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
local multiplier = provider:Create(amount, source)
|
|
176
|
+
multiplier.Parent = baseValue
|
|
177
|
+
|
|
178
|
+
return multiplier
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
function RogueProperty:CreateAdditive(amount, source)
|
|
182
|
+
assert(type(amount) == "number", "Bad amount")
|
|
183
|
+
|
|
184
|
+
local provider = self._serviceBag:GetService(RogueAdditiveProvider)
|
|
185
|
+
local baseValue = self:_getBaseValueObject()
|
|
186
|
+
|
|
187
|
+
if not baseValue then
|
|
188
|
+
warn("Failed to get the baseValue to parent")
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
local multiplier = provider:Create(amount, source)
|
|
192
|
+
multiplier.Parent = baseValue
|
|
193
|
+
|
|
194
|
+
return multiplier
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
function RogueProperty:_observeModifiersBrio()
|
|
198
|
+
return RxBinderUtils.observeBoundChildClassesBrio(self._roguePropertyBinderGroups.RogueModifiers:GetAll(), self._adornee)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
function RogueProperty:__index(index)
|
|
202
|
+
if index == "Value" then
|
|
203
|
+
return self:GetValue()
|
|
204
|
+
elseif RogueProperty[index] then
|
|
205
|
+
return RogueProperty[index]
|
|
206
|
+
else
|
|
207
|
+
error(("Bad index %q"):format(tostring(index)))
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
function RogueProperty:_decodeValue(current)
|
|
212
|
+
return RoguePropertyUtils.decodeProperty(self._definition, current)
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
function RogueProperty:_encodeValue(current)
|
|
216
|
+
return RoguePropertyUtils.encodeProperty(self._definition, current)
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
return RogueProperty
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
--[=[
|
|
2
|
+
@class RoguePropertyUtils
|
|
3
|
+
]=]
|
|
4
|
+
|
|
5
|
+
local require = require(script.Parent.loader).load(script)
|
|
6
|
+
|
|
7
|
+
local JSONUtils = require("JSONUtils")
|
|
8
|
+
|
|
9
|
+
local RoguePropertyUtils = {}
|
|
10
|
+
|
|
11
|
+
function RoguePropertyUtils.decodeProperty(definition, value)
|
|
12
|
+
if definition:GetValueType() == "table" then
|
|
13
|
+
local ok, decoded, err = JSONUtils.jsonDecode(value)
|
|
14
|
+
if not ok then
|
|
15
|
+
warn(("Failed to decode current value of %s. %q"):format(definition:GetName(), tostring(err)))
|
|
16
|
+
return definition:GetDefaultValue()
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
return decoded
|
|
20
|
+
else
|
|
21
|
+
return value
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
function RoguePropertyUtils.encodeProperty(definition, value)
|
|
26
|
+
if definition:GetValueType() == "table" then
|
|
27
|
+
local ok, encoded, err = JSONUtils.jsonEncode(value)
|
|
28
|
+
if not ok then
|
|
29
|
+
warn(("Failed to encode current value of %s. %q"):format(definition:GetName(), tostring(err)))
|
|
30
|
+
return definition:GetEncodedDefaultValue()
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
return encoded
|
|
34
|
+
else
|
|
35
|
+
return value
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
return RoguePropertyUtils
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
--[=[
|
|
2
|
+
@class RogueBindersShared
|
|
3
|
+
]=]
|
|
4
|
+
|
|
5
|
+
local require = require(script.Parent.loader).load(script)
|
|
6
|
+
|
|
7
|
+
local BinderProvider = require("BinderProvider")
|
|
8
|
+
local Binder = require("Binder")
|
|
9
|
+
|
|
10
|
+
return BinderProvider.new(function(self, serviceBag)
|
|
11
|
+
self:Add(Binder.new("RogueMultiplier", require("RogueMultiplier"), serviceBag))
|
|
12
|
+
self:Add(Binder.new("RogueAdditive", require("RogueAdditive"), serviceBag))
|
|
13
|
+
end)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
--[=[
|
|
2
|
+
@class RoguePropertyBinderGroups
|
|
3
|
+
]=]
|
|
4
|
+
|
|
5
|
+
local require = require(script.Parent.loader).load(script)
|
|
6
|
+
|
|
7
|
+
local BinderGroup = require("BinderGroup")
|
|
8
|
+
local t = require("t")
|
|
9
|
+
|
|
10
|
+
return require("BinderGroupProvider").new(function(self)
|
|
11
|
+
self:Add("RogueModifiers", BinderGroup.new(
|
|
12
|
+
{},
|
|
13
|
+
t.interface({
|
|
14
|
+
GetObject = t.callback;
|
|
15
|
+
})
|
|
16
|
+
))
|
|
17
|
+
end)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
--[=[
|
|
2
|
+
@class RoguePropertyService
|
|
3
|
+
]=]
|
|
4
|
+
|
|
5
|
+
local require = require(script.Parent.loader).load(script)
|
|
6
|
+
|
|
7
|
+
local RunService = game:GetService("RunService")
|
|
8
|
+
|
|
9
|
+
local Signal = require("Signal")
|
|
10
|
+
local Maid = require("Maid")
|
|
11
|
+
local Observable = require("Observable")
|
|
12
|
+
|
|
13
|
+
local RoguePropertyService = {}
|
|
14
|
+
|
|
15
|
+
function RoguePropertyService:Init(serviceBag)
|
|
16
|
+
assert(not self._serviceBag, "Already initialized")
|
|
17
|
+
self._serviceBag = assert(serviceBag, "No serviceBag")
|
|
18
|
+
|
|
19
|
+
self._maid = Maid.new()
|
|
20
|
+
|
|
21
|
+
-- Internal
|
|
22
|
+
self._serviceBag:GetService(require("RogueBindersShared"))
|
|
23
|
+
self._roguePropertyBinderGroups = self._serviceBag:GetService(require("RoguePropertyBinderGroups"))
|
|
24
|
+
|
|
25
|
+
self._providers = {}
|
|
26
|
+
|
|
27
|
+
self.ProviderAddedEvent = Signal.new()
|
|
28
|
+
self._maid:GiveTask(self.ProviderAddedEvent)
|
|
29
|
+
|
|
30
|
+
-- Internal providers
|
|
31
|
+
self._serviceBag:GetService(require("RogueAdditiveProvider"))
|
|
32
|
+
self._serviceBag:GetService(require("RogueMultiplierProvider"))
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
function RoguePropertyService:AddProvider(provider)
|
|
36
|
+
self._roguePropertyBinderGroups.RogueModifiers:Add(provider:GetBinder())
|
|
37
|
+
|
|
38
|
+
table.insert(self._providers, provider)
|
|
39
|
+
|
|
40
|
+
self.ProviderAddedEvent:Fire(provider)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
function RoguePropertyService:GetProviders()
|
|
44
|
+
return self._providers
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
function RoguePropertyService:ObserveProviderList()
|
|
48
|
+
return Observable.new(function(sub)
|
|
49
|
+
local maid = Maid.new()
|
|
50
|
+
|
|
51
|
+
sub:Fire(self._providers)
|
|
52
|
+
|
|
53
|
+
maid:GiveTask(self.ProviderAddedEvent:Connect(function()
|
|
54
|
+
sub:Fire(self._providers)
|
|
55
|
+
end))
|
|
56
|
+
|
|
57
|
+
return maid
|
|
58
|
+
end)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
function RoguePropertyService:CanInitializeProperties()
|
|
62
|
+
return RunService:IsServer()
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
function RoguePropertyService:Destroy()
|
|
66
|
+
self._maid:DoCleaning()
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
return RoguePropertyService
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
--[=[
|
|
2
|
+
@class RoguePropertyTable
|
|
3
|
+
]=]
|
|
4
|
+
|
|
5
|
+
local require = require(script.Parent.loader).load(script)
|
|
6
|
+
|
|
7
|
+
local RunService = game:GetService("RunService")
|
|
8
|
+
|
|
9
|
+
local RoguePropertyTable = {}
|
|
10
|
+
RoguePropertyTable.ClassName = "RoguePropertyTable"
|
|
11
|
+
RoguePropertyTable.__index = RoguePropertyTable
|
|
12
|
+
|
|
13
|
+
function RoguePropertyTable.new(adornee, serviceBag, roguePropertyTableDefinition)
|
|
14
|
+
local self = setmetatable({}, RoguePropertyTable)
|
|
15
|
+
|
|
16
|
+
self._adornee = assert(adornee, "No roguePropertyTableDefinition")
|
|
17
|
+
self._serviceBag = assert(serviceBag, "No serviceBag")
|
|
18
|
+
self._definition = assert(roguePropertyTableDefinition, "No roguePropertyTableDefinition")
|
|
19
|
+
|
|
20
|
+
self._properties = {}
|
|
21
|
+
|
|
22
|
+
if RunService:IsServer() then
|
|
23
|
+
self:_setup()
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
return self
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
function RoguePropertyTable:ObserveContainerBrio()
|
|
30
|
+
return self._definition:ObserveContainerBrio(self._serviceBag, self._adornee)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
function RoguePropertyTable:GetContainer()
|
|
34
|
+
return self._definition:GetContainer(self._serviceBag, self._adornee)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
function RoguePropertyTable:Set(newBaseValues)
|
|
38
|
+
assert(type(newBaseValues) == "table", "Bad newBaseValues")
|
|
39
|
+
|
|
40
|
+
for propertyName, value in pairs(newBaseValues) do
|
|
41
|
+
local rogueProperty = self:GetRogueProperty(propertyName)
|
|
42
|
+
if not rogueProperty then
|
|
43
|
+
error(("Bad property %q"):format(tostring(propertyName)))
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
rogueProperty:SetBaseValue(value)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
function RoguePropertyTable:_setup()
|
|
51
|
+
for definitionName, _ in pairs(self._definition:GetDefinitionMap()) do
|
|
52
|
+
self:GetRogueProperty(definitionName)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
function RoguePropertyTable:GetRogueProperty(name)
|
|
57
|
+
-- Caching these things doesn't do a whole lot, but saves on table allocation.
|
|
58
|
+
if self._properties[name] then
|
|
59
|
+
return self._properties[name]
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
local definition = self._definition:GetDefinition(name)
|
|
63
|
+
if definition then
|
|
64
|
+
self._properties[name] = definition:Get(self._serviceBag, self._adornee)
|
|
65
|
+
return self._properties[name]
|
|
66
|
+
else
|
|
67
|
+
return nil
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
function RoguePropertyTable:__index(index)
|
|
72
|
+
assert(type(index) == "string", "Bad index")
|
|
73
|
+
|
|
74
|
+
if RoguePropertyTable[index] then
|
|
75
|
+
return RoguePropertyTable[index]
|
|
76
|
+
elseif type(index) == "string" then
|
|
77
|
+
local property = self:GetRogueProperty(index)
|
|
78
|
+
if not property then
|
|
79
|
+
error(("Bad index %q"):format(tostring(index)))
|
|
80
|
+
end
|
|
81
|
+
return property
|
|
82
|
+
else
|
|
83
|
+
error(("Bad index %q"):format(tostring(index)))
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
return RoguePropertyTable
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "RoguePropertiesTest",
|
|
3
|
+
"tree": {
|
|
4
|
+
"$className": "DataModel",
|
|
5
|
+
"ServerScriptService": {
|
|
6
|
+
"rogueproperties": {
|
|
7
|
+
"$path": ".."
|
|
8
|
+
},
|
|
9
|
+
"Script": {
|
|
10
|
+
"$path": "scripts/Server"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"StarterPlayer": {
|
|
14
|
+
"StarterPlayerScripts": {
|
|
15
|
+
"Main": {
|
|
16
|
+
"$path": "scripts/Client"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
--[[
|
|
2
|
+
@class ClientMain
|
|
3
|
+
]]
|
|
4
|
+
local packages = game:GetService("ReplicatedStorage"):WaitForChild("Packages")
|
|
5
|
+
|
|
6
|
+
local serviceBag = require(packages.ServiceBag).new()
|
|
7
|
+
serviceBag:GetService(packages.RoguePropertyService)
|
|
8
|
+
|
|
9
|
+
-- Start game
|
|
10
|
+
serviceBag:Init()
|
|
11
|
+
serviceBag:Start()
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
--[[
|
|
2
|
+
@class ServerMain
|
|
3
|
+
]]
|
|
4
|
+
local ServerScriptService = game:GetService("ServerScriptService")
|
|
5
|
+
|
|
6
|
+
local loader = ServerScriptService:FindFirstChild("LoaderUtils", true).Parent
|
|
7
|
+
local packages = require(loader).bootstrapGame(ServerScriptService.rogueproperties)
|
|
8
|
+
|
|
9
|
+
local serviceBag = require(packages.ServiceBag).new()
|
|
10
|
+
serviceBag:GetService(packages.RoguePropertyService)
|
|
11
|
+
|
|
12
|
+
-- Start game
|
|
13
|
+
serviceBag:Init()
|
|
14
|
+
serviceBag:Start()
|
|
15
|
+
|
|
16
|
+
local RoguePropertyTableDefinition = require(packages.RoguePropertyTableDefinition)
|
|
17
|
+
local RoguePropertyDefinition = require(packages.RoguePropertyDefinition)
|
|
18
|
+
|
|
19
|
+
local properties = RoguePropertyTableDefinition.new({
|
|
20
|
+
RoguePropertyDefinition.new("AttackDamage", 30);
|
|
21
|
+
RoguePropertyDefinition.new("AbilityPower", 30);
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
local attackDamage = properties.AttackDamage:Get(serviceBag, workspace)
|
|
25
|
+
local abilityPower = properties.AbilityPower:Get(serviceBag, workspace)
|
|
26
|
+
|
|
27
|
+
attackDamage:Observe():Subscribe(function(value)
|
|
28
|
+
print("Attack damage", value)
|
|
29
|
+
end)
|
|
30
|
+
abilityPower:Observe():Subscribe(function(value)
|
|
31
|
+
print("Ability power", value)
|
|
32
|
+
end)
|
|
33
|
+
|
|
34
|
+
attackDamage:CreateMultiplier(2, workspace)
|
|
35
|
+
attackDamage:CreateAdditive(100, workspace)
|
|
36
|
+
|
|
37
|
+
print(attackDamage:GetValue())
|
|
38
|
+
|
|
39
|
+
attackDamage:ObserveSourcesBrio():Subscribe(function(value)
|
|
40
|
+
print(value:GetValue())
|
|
41
|
+
end)
|