300 lines
7.4 KiB
Lua
300 lines
7.4 KiB
Lua
local module = {};
|
|
|
|
--[[
|
|
this feature allows get object without making some created callback and local var
|
|
but, it can't allows mulit id and object
|
|
this feature should be upgraded
|
|
]]
|
|
local wrap = coroutine.wrap;
|
|
local insert = table.insert;
|
|
local remove = table.remove;
|
|
local gmatch = string.gmatch;
|
|
local gsub = string.gsub;
|
|
|
|
local function catch(...)
|
|
local passed,err = pcall(...);
|
|
if not passed then
|
|
warn ("[QUAD] Error occured while operating async task\n" .. tostring(err));
|
|
end
|
|
end
|
|
|
|
local week = {__mode = "v"};
|
|
|
|
---@param shared quad_export
|
|
---@return quad_module_store
|
|
function module.init(shared)
|
|
---@class quad_module_store
|
|
local new = {__type = "quad_module_store"};
|
|
local items = shared.items;
|
|
|
|
-- id space (array of object)
|
|
local objectListClass = {__type = "quad_objectlist"};
|
|
function objectListClass:each(func)
|
|
local index = 1;
|
|
for i,v in pairs(self) do
|
|
wrap(catch)(func,index,v);
|
|
index = index + 1;
|
|
end
|
|
end
|
|
function objectListClass:eachSync(func)
|
|
local index = 1;
|
|
for _,v in pairs(self) do
|
|
local ret = func(index,v);
|
|
index = index + 1;
|
|
if ret then
|
|
break;
|
|
end
|
|
end
|
|
end
|
|
function objectListClass:remove(indexOrItem)
|
|
local thisType = type(indexOrItem);
|
|
if thisType == "number" then
|
|
remove(self,indexOrItem);
|
|
else
|
|
for i,v in pairs(self) do
|
|
if v == indexOrItem then
|
|
remove(self,i);
|
|
break;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
function objectListClass:isEmpty()
|
|
if next(self) then return true; end
|
|
return false;
|
|
end
|
|
function objectListClass.__new()
|
|
return setmetatable({},objectListClass);
|
|
end
|
|
function objectListClass:__newIndex(key,value) -- props setter
|
|
self:each(function (this)
|
|
this[key] = value;
|
|
end);
|
|
end
|
|
objectListClass.__mode = "kv"; -- week link for gc
|
|
objectListClass.__index = objectListClass;
|
|
|
|
-- get object array with id (objSpace)
|
|
function new.getObjects(id)
|
|
local list = items[id];
|
|
if list then return list; end
|
|
list = objectListClass.__new(id);
|
|
items[id] = list;
|
|
return list;
|
|
end
|
|
-- get first object with id (not array)
|
|
function new.getObject(id)
|
|
local item = items[id];
|
|
return item and item[next(item)];
|
|
end
|
|
--TODO: if item is exist already, ignore this call
|
|
-- adding object with id
|
|
function new.addObject(ids,object)
|
|
for id in gmatch(ids,"[^,]+") do -- split by ,
|
|
-- remove trailing, heading spaces
|
|
id = gsub(gsub(id,"^ +","")," +$","");
|
|
local array = items[id];
|
|
if not array then
|
|
array = objectListClass.__new();
|
|
items[id] = array;
|
|
end
|
|
insert(array, object);
|
|
end
|
|
end
|
|
|
|
local registerClass = {
|
|
__type = "quad_register";
|
|
register = function (s,efunc)
|
|
local self = s.store;
|
|
local events = self.__evt;
|
|
for key in s.key:gmatch("[^,]+") do
|
|
key = key:gsub("^ +",""):gsub(" +$","");
|
|
local event = events[key];
|
|
if not event then
|
|
event = setmetatable({},week);
|
|
events[key] = event;
|
|
end
|
|
insert(event,efunc);
|
|
end
|
|
end;
|
|
with = function (s,wfunc)
|
|
-- s.wfunc = wfunc;
|
|
-- return s;
|
|
return setmetatable({wfunc = wfunc},{__index = s});
|
|
end;
|
|
default = function (s,dvalue)
|
|
-- s.dvalue = dvalue;
|
|
-- return s;
|
|
return setmetatable({dvalue = dvalue},{__index = s});
|
|
end;
|
|
tween = function (s,tvalue)
|
|
-- s.tvalue = tvalue;
|
|
-- return s;
|
|
return setmetatable({tvalue = tvalue},{__index = s});
|
|
end;
|
|
---@deprecated
|
|
from = function (s,fvalue)
|
|
warn "[QUAD] register:from() is deprecated. You can use register:add(t:table|function) instead";
|
|
-- s.fvalue = fvalue;
|
|
-- return s;
|
|
return setmetatable({fvalue = fvalue},{__index = s});
|
|
end;
|
|
add = function (s,avalue)
|
|
-- s.avalue = avalue;
|
|
-- return s;
|
|
return setmetatable({avalue = avalue},{__index = s});
|
|
end;
|
|
-- return init data
|
|
calcWithDefault = function (s,withItem)
|
|
local with = s.wfunc;
|
|
local tstore = s.store;
|
|
local rawKey = s.key;
|
|
local tween = s.tvalue or s.dtvalue;
|
|
local from = s.fvalue;
|
|
local add = s.avalue;
|
|
local set = tstore[rawKey];
|
|
if set ~= nil or (rawKey:match(",") and with) then
|
|
if add then
|
|
set = set + add;
|
|
end
|
|
if from then
|
|
set = from[set];
|
|
end
|
|
if with then
|
|
set = with(tstore,set,rawKey,withItem);
|
|
end
|
|
return set,tween;
|
|
else
|
|
local dset = s.dvalue;
|
|
if dset ~= nil then
|
|
return dset,tween;
|
|
end
|
|
warn "[Quad] no default value found.";
|
|
end
|
|
end;
|
|
calcWithNewValue = function(s,withItem,newValue,key)
|
|
local with = s.wfunc;
|
|
local tstore = s.store;
|
|
local tween = s.tvalue or s.dtvalue;
|
|
local from = s.fvalue;
|
|
local add = s.avalue;
|
|
if add then
|
|
newValue = newValue + add;
|
|
end
|
|
if from then
|
|
newValue = from[newValue];
|
|
end
|
|
if with then
|
|
newValue = with(tstore,newValue,key,withItem);
|
|
end
|
|
return newValue,tween;
|
|
end
|
|
};
|
|
registerClass.__index = registerClass;
|
|
|
|
-- bindable store object
|
|
local store = {__type = "quad_store"};
|
|
local storeIdSpace = {};
|
|
function store:__index(key)
|
|
local this = self.__self[key];
|
|
if this ~= nil then
|
|
return this;
|
|
end
|
|
return store[key];
|
|
end
|
|
function store:__newindex(key,value)
|
|
-- if got register, just copy data to self and connect
|
|
if type(value) == "table" and value.__type == "quad_register" then
|
|
warn "[Quad] adding register value on store is only allowed when init store. set value request was ignored";
|
|
return;
|
|
end
|
|
self.__self[key] = value;
|
|
local event = self.__evt[key];
|
|
if event then
|
|
for _,v in pairs(event) do -- NO ipairs here
|
|
wrap(catch)(v,store,value,key);
|
|
end
|
|
end
|
|
end
|
|
-- init bindings
|
|
function new.__initStoreRegisterBinding(self,withItem)
|
|
local selfTweens = self.__tweens;
|
|
local selfValues = self.__self;
|
|
for key,item in pairs(selfValues) do
|
|
if type(item) == "table" and item.__type == "quad_register" then
|
|
-- fetch data from origin
|
|
do
|
|
local tstore = item.store;
|
|
for tkey in item.key:gmatch("^[,]") do
|
|
selfValues[tkey] = tstore[tkey];
|
|
end
|
|
end
|
|
|
|
-- calc value
|
|
do
|
|
local setValue,tween = item:calcWithDefault(withItem);
|
|
selfValues[key] = setValue;
|
|
selfTweens[key] = tween;
|
|
end
|
|
|
|
-- make event connection
|
|
item:register(function (_,newValue,eventKey)
|
|
-- !HOLD IT SELF TO PREVENT THIS REGISTER BEGIN REMOVED FROM MEMORY
|
|
local setValue = item:calcWithNewValue(withItem,newValue,eventKey);
|
|
self[key] = setValue;
|
|
end);
|
|
end
|
|
end
|
|
end
|
|
-- create register
|
|
function store:__call(key,func)
|
|
local last = self.__self[key];
|
|
if last and type(last) == "table" and getmetatable(last) == registerClass then
|
|
return last;
|
|
end
|
|
local register = self.__reg[key];
|
|
local tweens = self.__tweens;
|
|
if not register then
|
|
register = setmetatable({
|
|
key = key;
|
|
store = self;
|
|
dtvalue = tweens and tweens[key];
|
|
},registerClass);
|
|
self.__reg[key] = register;
|
|
end
|
|
|
|
if func then
|
|
register.register(func);
|
|
end
|
|
return register;
|
|
end
|
|
function store:default(key,value)
|
|
local old = self[key];
|
|
if old == nil then
|
|
self[key] = value;
|
|
return;
|
|
end
|
|
end
|
|
function new.new(self,id)
|
|
local this = setmetatable(
|
|
{
|
|
__self = self or {}, -- real value storage
|
|
__evt = {}, -- changed event handler functions (dict of array)
|
|
__reg = setmetatable({},week), -- save registers
|
|
__tweens = {} -- tweens (if inited with register, save tween and use as default tween data)
|
|
},store
|
|
);
|
|
if id then -- save in id space
|
|
storeIdSpace[id] = this;
|
|
end
|
|
return this;
|
|
end
|
|
local storeNew = new.new;
|
|
function new.getStore(id)
|
|
return storeIdSpace[id] or storeNew({},id);
|
|
end
|
|
|
|
return new;
|
|
end
|
|
|
|
return module;
|