-- Compiled with roblox-ts v3.0.0 local TS = _G[script] -- eslint-disable @typescript-eslint/no-explicit-any local Reflect = TS.import(script, TS.getModule(script, "@flamework", "core").out).Reflect local _components = TS.import(script, TS.getModule(script, "@flamework", "components").out) local Component = _components.Component local BaseComponent = _components.BaseComponent local ReconcileTable = TS.import(script, script.Parent.Parent.Parent, "utility").ReconcileTable local Janitor = TS.import(script, TS.getModule(script, "@rbxts", "janitor").src).Janitor local InjectTycoon = TS.import(script, script.Parent.Parent, "decorators", "Inject-tycoon").InjectTycoon local getIdFromSpecifier = TS.import(script, TS.getModule(script, "@flamework", "components").out.utility).getIdFromSpecifier local isConstructor = TS.import(script, TS.getModule(script, "@flamework", "core").out.utility).isConstructor local t = TS.import(script, TS.getModule(script, "@rbxts", "t").lib.ts).t local Signal = TS.import(script, TS.getModule(script, "@rbxts", "signals-tooling").out).Signal local DecoratedTycoonItems = {} --[[ * * Register a class as a tycoon item. * * @metadata reflect identifier flamework:implements flamework:parameters injectable macro ]] local TycoonItem = function(config, metadata) local _metadata = metadata assert(_metadata, "metadata is required") return function(ctor) local _condition = isConstructor(ctor) if _condition then local _ctor = ctor DecoratedTycoonItems[_ctor] = true _condition = DecoratedTycoonItems end Reflect.defineMetadata(ctor, "config", config) Reflect.defineMetadata(ctor, "metadata", metadata) local _exp = ctor local _object = { instanceGuard = metadata.InstanceGuard, attributes = metadata.AttributesGuard, } local _left = "defaults" local _result = config if _result ~= nil then _result = _result.defaults end _object[_left] = _result Reflect.decorate(_exp, "$c:components@Component", Component, { _object }) end end local BaseTycoonItem do local super = BaseComponent BaseTycoonItem = setmetatable({}, { __tostring = function() return "BaseTycoonItem" end, __index = super, }) BaseTycoonItem.__index = BaseTycoonItem function BaseTycoonItem:constructor(TycoonService, logger, components) super.constructor(self) self.TycoonService = TycoonService self.logger = logger self.components = components self.__janitor = Janitor.new() self.isLocked = false self.isDestroyed = false self.dataGuard = Reflect.getMetadata(getmetatable(self), "metadata").DataGuard self.LockChanged = Signal.new() end function BaseTycoonItem:onStart() self:initId() self.baseParent = self.instance.Parent end function BaseTycoonItem:onSetup() self.logger:Info(`{self:GetId()} is starting`) self:initLockState() self:initEvents() self:validateData() self.logger:Info(`{self.instance.Name} is created`) end function BaseTycoonItem:GetTycoon() return self._tycoon end function BaseTycoonItem:initEvents() self.__janitor:Add(self._tycoon.DataResetted:Connect(function() return self:initLockState() end), "Disconnect") end function BaseTycoonItem:validateData() local data = self:TryGetData() if not (data ~= 0 and data == data and data ~= "" and data) or self.dataGuard(data) then return nil end local template = self:generateData() local newData = ReconcileTable(data, template) self:mutateData(newData) end function BaseTycoonItem:initLockState() local _value = self:hasUnlockedTag() or self:TryGetData() if _value ~= 0 and _value == _value and _value ~= "" and _value then self.isLocked = true self:Appear() return nil end self.isLocked = false self:Disappear() end function BaseTycoonItem:hasUnlockedTag() return self.instance:HasTag(self.TycoonService:GetUnlockedItemTag()) end function BaseTycoonItem:initId() local _condition = self.attributes.Id if _condition == nil then _condition = self:buildId() end self.id = _condition self.logger:Info(`ID: {self.id} for {self.instance.Name}`) end function BaseTycoonItem:fallbackPath() self.logger:Warn(`Advanced path build failure for object {self.instance.Name}, defaulting to name`) return self.instance.Name end function BaseTycoonItem:buildId() local tycoonInstance = self:GetTycoon().instance local path = {} local currentParent = self.instance while currentParent ~= tycoonInstance do local _array = { currentParent } local _length = #_array table.move(path, 1, #path, _length + 1, _array) path = _array if not currentParent.Parent then return self:fallbackPath() end currentParent = currentParent.Parent end local _array = { tycoonInstance } local _length = #_array table.move(path, 1, #path, _length + 1, _array) path = _array local stringPath = "" -- ▼ ReadonlyArray.forEach ▼ local _callback = function(part, index) stringPath ..= part.Name if index + 1 ~= #path then stringPath ..= "/" end end for _k, _v in path do _callback(_v, _k - 1, path) end -- ▲ ReadonlyArray.forEach ▲ return stringPath end function BaseTycoonItem:mutateData(newData) self.logger:Assert(not self.isLocked, "Item is locked") self._tycoon:MutateItemData(self.id, newData) end function BaseTycoonItem:clearData() self._tycoon:ClearItemData(self.id) end function BaseTycoonItem:GetId() return self.id end function BaseTycoonItem:GetData() self.logger:Assert(not self.isLocked, "Item is locked") local _items = self._tycoon:GetData().Items local _id = self.id return _items[_id] end function BaseTycoonItem:TryGetData() local _items = self._tycoon:GetData().Items local _id = self.id return _items[_id] end function BaseTycoonItem:IsLocked() return self.isLocked end BaseTycoonItem.onAppear = TS.async(function(self) end) BaseTycoonItem.onDisappear = TS.async(function(self) end) function BaseTycoonItem:onDestroyed() end function BaseTycoonItem:Disappear() if self.isLocked or self.isDestroyed then return nil end self.isLocked = true self:setParentWhenLocked() self:clearData() self.LockChanged:fire(true) self:onDisappear() end function BaseTycoonItem:Appear() if not self.isLocked or self.isDestroyed then return nil end self.isLocked = false self:setParentWhenUnlocked() self:mutateData(self:generateData()) self.LockChanged:fire(false) self:onAppear() end function BaseTycoonItem:setParentWhenUnlocked() self.instance.Parent = self.baseParent end function BaseTycoonItem:setParentWhenLocked() self.instance.Parent = self._tycoon:GetContainer() end function BaseTycoonItem:Destroy() self.components:removeComponent(self.instance, getIdFromSpecifier(getmetatable(self))) end function BaseTycoonItem:destroy() self.isDestroyed = true self.__janitor:Destroy() self:onDestroyed() setmetatable(self, { __index = function(t, index) error(`Item destroyed.`) end, }) end do -- (Flamework) BaseTycoonItem metadata Reflect.defineMetadata(BaseTycoonItem, "identifier", "@rbxts/flamework-tycoon:source/item/BaseTycoonItem@BaseTycoonItem") Reflect.defineMetadata(BaseTycoonItem, "flamework:parameters", { "@rbxts/flamework-tycoon:source/TycoonService@TycoonService", "@rbxts/flamework-tycoon:source/TycoonLogger@TycoonLogger", "$c:components@Components" }) Reflect.defineMetadata(BaseTycoonItem, "flamework:implements", { "$:flamework@OnStart" }) -- (Flamework) BaseTycoonItem._tycoon metadata Reflect.defineMetadata(BaseTycoonItem, "flamework:type", "@rbxts/flamework-tycoon:source/BaseTycoonComponent@BaseTycoonComponent", "_tycoon") end InjectTycoon(BaseTycoonItem, "_tycoon") end -- (Flamework) BaseTycoonItem decorators Reflect.decorate(BaseTycoonItem, "$c:components@Component", Component, { { attributes = { Id = t.optional(t.string), }, instanceGuard = t.instanceIsA("Instance"), } }) return { DecoratedTycoonItems = DecoratedTycoonItems, TycoonItem = TycoonItem, BaseTycoonItem = BaseTycoonItem, }