初始化提交

This commit is contained in:
2025-07-09 01:08:35 +08:00
parent d3296791cf
commit 62e0f56c60
618 changed files with 173543 additions and 0 deletions

33
Content/Lua/.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,33 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "lua",
"request": "launch",
"tag": "normal",
"name": "LuaPanda",
"cwd": "${workspaceFolder}",
"luaFileExtension": "",
"connectionPort": 8818,
"stopOnEntry": true,
"useCHook": true,
"autoPathMode": true,
},
{
"type": "lua",
"request": "launch",
"tag": "independent_file",
"name": "LuaPanda-IndependentFile",
"luaPath": "",
"packagePath": [],
"luaFileExtension": "",
"connectionPort": 8820,
"stopOnEntry": true,
"useCHook": true,
}
]
}

View File

@ -0,0 +1,38 @@
---@enum EBusyRoleState
local EBusyRoleState = {
BonfireIdle = 0,
Searching = 1,
Picking = 2,
PickFinished = 3,
BackBonfire = 4
}
---@enum ERoleMoveDirection
local ERoleMoveDirection = {
Move_Right = 0,
Move_Left = 1,
Move_All_Cnt = 2
};
---@enum EBusyItemEffectType
local EBusyItemEffectType = {
Health = 0,
Hunger = 1,
Speed = 2,
};
---@enum EBusyAnimationPhase
local EBusyAnimationPhase = {
PrepareCast = 0,
Casting = 1,
PostCast = 2,
};
---@enum EWidgetLayoutType
local EWidgetLayoutType = {
MainLayer = 0,
PopupLayer = 1,
FloatLayer = 2,
TopLayer = 3,
LayerTypeMax = 4
};

View File

@ -0,0 +1,24 @@
-- 这个返回值固定返回的是string我希望当我使用import("ERoleState")时它返回的是ERoleState的注解或者是ERoleState.lua这个文件返回值的注解
-- ---自定义导入函数功能类似require但支持额外特性
-- ---@generic T
-- ---@param modulePath T 模块路径或预加载的模块
-- ---@param hotReload? boolean 是否启用热重载
-- ---@return T 返回加载的模块
-- function import(modulePath, hotReload) end
---自定义模块导入函数,支持类型感知的模块加载
---@generic T : string -- 限定modulePath为字符串类型
---@param modulePath `T` 模块路径(如"ERoleState"
---@param hotReload? boolean 是否启用热重载
---@return T 返回对应模块的类型
---@error 当模块加载失败时抛出错误
function import(modulePath, hotReload) end
function Class(a, b, c) end
---@class slua
slua = {
createDelegate = function(func) end
}

View File

@ -0,0 +1,8 @@
---@enum ESlateVisibility
local ESlateVisibility = {
Visible = 0,
Collapsed = 1,
Hidden = 2,
HitTestInvisible = 3,
SelfHitTestInvisible = 4
}

View File

@ -0,0 +1,22 @@
local PWClass = {}
local function MetaCall(cls, ...)
local inst = {
__CLASS = cls
}
cls.ctor(inst, ...)
return setmetatable(inst, cls)
end
function PWClass.derive(name, base)
local cls = {
__CLASS_NAME = name
}
cls.__index = cls
return setmetatable(cls, {
__call = MetaCall
})
end
return PWClass

View File

@ -0,0 +1,164 @@
local Module = {}
local ReactiveWatcherMeta = {}
local ReactivePropertyMeta = {}
local reactive_watcher_stack = {}
local auto_run_watcher_mapping = {}
local triggered_watchers = {} -- 由SetProperty激活的watcher列表
local function CreateReactiveWatcher(auto_run)
local watcher = rawget(auto_run_watcher_mapping, auto_run)
if watcher == nil then
watcher = {
auto_run = auto_run,
properties = {},
}
rawset(auto_run_watcher_mapping, auto_run, watcher)
setmetatable(watcher, ReactiveWatcherMeta)
end
return watcher
end
local function PushWatcher(watcher)
table.insert(reactive_watcher_stack, watcher)
end
local function PeekWatcher()
local watcher_stack_deepth = #reactive_watcher_stack
if watcher_stack_deepth > 0 then
return reactive_watcher_stack[watcher_stack_deepth]
else
return nil
end
end
local function PopWatcher()
local watcher = PeekWatcher()
if watcher then
table.remove(reactive_watcher_stack)
end
return watcher
end
local function AddPropertyToWatcher(watcher, property, key)
local properties = watcher.properties
local keys = properties[property] or {}
keys[key] = true
properties[property] = keys
end
local function ClearAllPropertiesOfWatcher(watcher)
local properties = watcher.properties
for property, keys in pairs(properties) do
local property_watchers = property.watchers
for key, state in pairs(keys) do
if state == true then
local key_watchers = property_watchers[key]
key_watchers[watcher] = nil
end
end
end
rawset(watcher, "properties", {})
end
local function RunFunctionOfWatcher(watcher)
local auto_run = watcher.auto_run
assert(auto_run ~= nil, "can't find auto run function")
ClearAllPropertiesOfWatcher(watcher) -- 解绑所有的binding由auto_run重新生成
PushWatcher(watcher)
auto_run()
local poped_watcher = PopWatcher()
assert(watcher == poped_watcher, "fatal")
end
local function GetReactiveProperty(property, key)
local meta = property.meta
local property_watchers = property.watchers
local current_watcher = PeekWatcher()
if current_watcher ~= nil then
local key_watchers = property_watchers[key] or {}
key_watchers[current_watcher] = true
property_watchers[key] = key_watchers
AddPropertyToWatcher(current_watcher, property, key)
end
return rawget(meta, key)
end
local function SetReactiveProperty(property, key, value)
local meta = property.meta
local property_watchers = property.watchers
rawset(meta, key, value)
local key_watchers = property_watchers[key] or {}
for watcher, state in pairs(key_watchers) do
if state == true then
triggered_watchers[watcher] = true
end
end
end
ReactiveWatcherMeta.__index = ReactiveWatcherMeta
ReactivePropertyMeta.__index = GetReactiveProperty
ReactivePropertyMeta.__newindex = SetReactiveProperty
-- 销毁Reactive Watcher
function ReactiveWatcherMeta:Destroy()
ClearAllPropertiesOfWatcher(self)
auto_run_watcher_mapping[self.auto_run] = nil
self.auto_run = nil
end
function ReactivePropertyMeta:Destroy()
local watchers = rawget(self, "watchers")
return watchers
end
-- 创建auto_run的watcher, 并运行auto_run函数
function Module.Watcher(auto_run)
local watcher = CreateReactiveWatcher(auto_run)
RunFunctionOfWatcher(watcher)
return watcher
end
function Module.ReactiveProperty(data)
local Property = {
meta = data or {},
watchers = {},
}
return setmetatable(Property, ReactivePropertyMeta)
end
function Module.RawGet(property, key)
local meta = rawget(property, "meta")
return rawget(meta, key)
end
function Module.RawSet(property, key, value)
local meta = rawget(property, "meta")
rawset(meta, key, value)
end
function Module.Process()
local limit = 64
while next(triggered_watchers) and limit > 0 do
local triggered_list = {}
for w, state in pairs(triggered_watchers) do
if state == true then table.insert(triggered_list, w) end
end
for _, w in ipairs(triggered_list) do
limit = limit - 1
triggered_watchers[w] = nil
RunFunctionOfWatcher(w)
end
end
end
return Module

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,222 @@
#ifndef LIBPDEBUG_H
#define LIBPDEBUG_H
//1.使用源码编译要打开宏USE_SOURCE_CODE. win下要设置LUA_INTEGER和lua版本号
#define LUA_DEBUGGER_NAME "LuaPanda" //debugger's name in LuaDebug.lua
#define HOOK_LIB_VERSION "3.2.0" //lib version
//#define USE_SOURCE_CODE //using source code to build
#if !defined(USE_SOURCE_CODE) && defined(_WIN32)
#define LUA_INTEGER long long //set LUA_INTEGER. In 501 is ptrdiff_t. 503 can set longlong(64bit) or int(32bit)
#define LUA_VERSION_NUM 503 //lua version used by WIN32 build lib. eg. 501,503
#endif
//setting end
#if !defined(USE_SOURCE_CODE) && defined(_WIN32)
#include <Windows.h>
#include <Tlhelp32.h>
#else
//2.如果lua源码是C++形式注释掉下面extern "C"
extern "C"{
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
#include "luaconf.h"
}
#endif
//3.如果lua代码在命名空间中要设置用户命名空间. 防止找不到lua方法
//using namespace slua;
#ifdef USE_SOURCE_CODE
extern "C" void pdebug_init(lua_State* L);
#endif
#if !defined(USE_SOURCE_CODE) && defined(_WIN32)
/*
** Lua - An Extensible Extension Language
** Lua.org, PUC-Rio, Brazil (http://www.lua.org)
** See Copyright Notice at the end of this file
*/
#if LUA_VERSION_NUM == 501
#define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, (s))
#endif
#define LUA_TNONE (-1)
#define LUA_TNIL 0
#define LUA_TBOOLEAN 1
#define LUA_TLIGHTUSERDATA 2
#define LUA_TNUMBER 3
#define LUA_TSTRING 4
#define LUA_TTABLE 5
#define LUA_TFUNCTION 6
#define LUA_TUSERDATA 7
#define LUA_TTHREAD 8
#define LUA_NUMBER double
#define LUA_REGISTRYINDEX (-10000)
#define LUA_ENVIRONINDEX (-10001)
#define LUA_GLOBALSINDEX (-10002)
#define lua_upvalueindex(i) (LUA_GLOBALSINDEX-(i))
#define LUA_IDSIZE 60
#define LUA_HOOKCALL 0
#define LUA_HOOKRET 1
#define LUA_HOOKLINE 2
#define LUA_HOOKCOUNT 3
#define LUA_HOOKTAILRET 4
#define LUA_MASKCALL (1 << LUA_HOOKCALL)
#define LUA_MASKRET (1 << LUA_HOOKRET)
#define LUA_MASKLINE (1 << LUA_HOOKLINE)
#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT)
#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL))
#define lua_tostring(L,i) lua_tolstring(L, (i), NULL)
#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE)
#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION)
#define lua_pop(L,n) lua_settop(L, -(n)-1)
#define lua_newtable(L) lua_createtable(L, 0, 0)
struct lua_State;
struct lua_Debug {
int event;
const char *name; /* (n) */
const char *namewhat; /* (n) `global', `local', `field', `method' */
const char *what; /* (S) `Lua', `C', `main', `tail' */
const char *source; /* (S) */
int currentline; /* (l) */
int nups; /* (u) number of upvalues */
int linedefined; /* (S) */
int lastlinedefined; /* (S) */
char short_src[LUA_IDSIZE]; /* (S) */
/* private part */
int i_ci; /* active function */
};
typedef LUA_INTEGER lua_Integer;
typedef LUA_NUMBER lua_Number;
typedef int (*lua_CFunction) (lua_State *L);
typedef struct luaL_Reg {
const char *name;
lua_CFunction func;
} luaL_Reg;
#define LUA_KCONTEXT ptrdiff_t
typedef LUA_KCONTEXT lua_KContext;
//lua function
typedef lua_Integer(*luaDLL_checkinteger) (lua_State *L, int numArg);
typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
typedef int (*lua_KFunction) (lua_State *L, int status, lua_KContext ctx);
typedef const lua_Number *(*luaDLL_version)(lua_State *L);
typedef void (*luaLDLL_register)(lua_State *L, const char *libname, const luaL_Reg *l);
typedef int (*luaDLL_gettop)(lua_State *L);
typedef const char *(*luaDLL_pushstring)(lua_State *L, const char *s);
typedef int (*luaDLL_settop)(lua_State *L, int idx);
typedef int (*luaDLL_tointeger)(lua_State *L, int idx);
typedef int (*luaDLL_next)(lua_State *L, int idx);
typedef int (*luaDLL_pcall)(lua_State *L, int nargs, int nresults, int errfunc);
typedef void (*luaDLL_pushnil)(lua_State *L);
typedef void (*luaDLL_getfield)(lua_State *L, int idx, const char *k);
typedef int (*luaDLL_getinfo)(lua_State *L, const char *what, void *ar);
typedef void (*luaDLL_pushinteger) (lua_State *L, lua_Integer n);
#if LUA_VERSION_NUM == 501
typedef int(*luaDLL_sethook)(lua_State *L, void* func, int mask, int count);
#else
typedef void (*luaDLL_sethook)(lua_State *L, lua_Hook f, int mask, int count);
#endif
typedef void (*luaDLL_pushnumber)(lua_State *L, lua_Number n);
typedef lua_Number (*luaDLL_checknumber)(lua_State *L, int narg);
typedef const char *(*luaDLL_checklstring)(lua_State *L, int narg, size_t *len);
typedef const char *(*luaDLL_tolstring)(lua_State *L, int idx, size_t *len);
typedef int (*luaDLL_type)(lua_State *L, int idx);
//5.3
typedef void (*luaDLL_createtable)(lua_State *L, int narray, int nrec);
typedef void (*luaDLL_setfuncs)(lua_State *L, const luaL_Reg *l, int nup);
typedef lua_Integer(*luaDLL_tointegerx)(lua_State *L, int idx, int *pisnum);
typedef int (*luaDLL_getglobal)(lua_State *L, const char *name);
typedef int (*luaDLL_pcallk)(lua_State *L, int nargs, int nresults, int msgh, lua_KContext ctx, lua_KFunction k);
typedef int (*luaDLL_toboolean)(lua_State *L, int index);
luaDLL_checkinteger luaL_checkinteger;
luaDLL_version lua_version;
luaDLL_gettop lua_gettop;
luaDLL_pushstring lua_pushstring;
luaLDLL_register luaL_register;
luaDLL_settop lua_settop;
luaDLL_pcall lua_pcall;
luaDLL_pushnumber lua_pushnumber;
luaDLL_checklstring luaL_checklstring;
luaDLL_tointeger lua_tointeger;
luaDLL_pushnil lua_pushnil;
luaDLL_getfield lua_getfield;
luaDLL_next lua_next;
luaDLL_getinfo lua_getinfo;
luaDLL_sethook lua_sethook;
luaDLL_checknumber luaL_checknumber;
luaDLL_type lua_type;
luaDLL_tolstring lua_tolstring;
luaDLL_pushinteger lua_pushinteger;
luaDLL_toboolean lua_toboolean;
//
HMODULE hInstLibrary;
//slua-ue header
#if LUA_VERSION_NUM > 501
//5.3
luaDLL_createtable lua_createtable;
luaDLL_setfuncs luaL_setfuncs;
luaDLL_tointegerx lua_tointegerx;
luaDLL_getglobal lua_getglobal;
luaDLL_pcallk lua_pcallk;
#define lua_pcall(L,n,r,f) lua_pcallk(L, (n), (r), (f), 0, NULL)
#define lua_tointeger(L,i) lua_tointegerx(L,(i),NULL);
#define PURE_API =0
namespace slua {
struct LuaInterface {
virtual const lua_Number *lua_version(lua_State *L) PURE_API;
virtual const char *lua_pushstring(lua_State *L, const char *s) PURE_API;
virtual int lua_gettop(lua_State *L) PURE_API;
virtual void lua_settop(lua_State *L, int index) PURE_API;
virtual int lua_pcallk(lua_State *L, int nargs, int nresults, int msgh, lua_KContext ctx, lua_KFunction k) PURE_API;
virtual void lua_pushnumber(lua_State *L, lua_Number n) PURE_API;
virtual const char *luaL_checklstring(lua_State *L, int arg, size_t *l) PURE_API;
virtual const char *lua_tolstring(lua_State *L, int index, size_t *len) PURE_API;
virtual int lua_type(lua_State *L, int index) PURE_API;
virtual lua_Integer lua_tointegerx(lua_State *L, int index, int *isnum) PURE_API;
virtual void lua_pushnil(lua_State *L) PURE_API;
virtual int lua_getfield(lua_State *L, int index, const char *k) PURE_API;
virtual int lua_next(lua_State *L, int index) PURE_API;
virtual int lua_getinfo(lua_State *L, const char *what, lua_Debug *ar) PURE_API;
virtual void lua_sethook(lua_State *L, lua_Hook f, int mask, int count) PURE_API;
virtual lua_Number luaL_checknumber(lua_State *L, int arg) PURE_API;
virtual void lua_createtable(lua_State *L, int narr, int nrec) PURE_API;
virtual void luaL_setfuncs(lua_State *L, const luaL_Reg *l, int nup) PURE_API;
virtual int lua_getglobal(lua_State *L, const char *name) PURE_API;
virtual int lua_toboolean(lua_State *L, int index) PURE_API;
};
}
typedef slua::LuaInterface* (*dll_GetLuaInterface)();
dll_GetLuaInterface getInter;
#endif //LUA_VERSION_NUM > 501
#endif //_WIN32
#endif //LIBPDEBUG_H
/******************************************************************************
* Copyright (C) 1994-2008 Lua.org, PUC-Rio. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/

View File

@ -0,0 +1,10 @@
local GameInstance = {}
local Reactive = require("Core.Reactive")
function GameInstance.OnEndFrame()
Reactive.Process()
end
return GameInstance

View File

@ -0,0 +1,27 @@
local Ability = {}
local GetGameplayTag = require("GamePlay.Utils").GetGameplayTag
local AbilitySystemBlueprintLibrary = import("AbilitySystemBlueprintLibrary")
function Ability:K2_ActivateAbilityFromEvent(_)
local RoleUtils = require("GamePlay.Utils.RoleUtils")
local owner = self:GetOwningActorFromActorInfo()
local asc = AbilitySystemBlueprintLibrary.GetAbilitySystemComponent(owner)
local increase_handle = asc:MakeOutgoingSpec(
self.DefaultEffectConfig, 1, asc:MakeEffectContext()
)
AbilitySystemBlueprintLibrary.AssignTagSetByCallerMagnitude(
increase_handle, GetGameplayTag("Change.Role.MoveSpeed"), self.SpeedIncrease
)
asc:BP_ApplyGameplayEffectSpecToSelf(increase_handle)
RoleUtils.ChangeHunger(owner, -100)
end
-- function Ability:K2_OnEndAbility(bWasCancelled)
-- print(bWasCancelled, "Ability:K2_OnEndAbility")
-- end
return Class(nil, nil, Ability)

View File

@ -0,0 +1,29 @@
local EatFoodAbility = {}
local Utils = require("GamePlay.Utils")
local RoleUtils = require("GamePlay.Utils.RoleUtils")
local function HandleHealthChange(role, effects)
local tag = Utils.GetGameplayTag("Change.Role.Health")
local value = effects:Get(tag)
if value ~= nil then
RoleUtils.ChangeHealth(role, value)
end
end
local function HandleHungerChange(role, effects)
local tag = Utils.GetGameplayTag("Change.Role.Hunger")
local value = effects:Get(tag)
if value ~= nil then
RoleUtils.ChangeHunger(role, value)
end
end
function EatFoodAbility:K2_ActivateAbilityFromEvent(EventData)
local item_config = Utils.GetItemConfigByID(math.floor(EventData.EventMagnitude))
local owner = self:GetOwningActorFromActorInfo()
HandleHealthChange(owner, item_config.GameplayEffects)
HandleHungerChange(owner, item_config.GameplayEffects)
self:K2_EndAbility()
end
return Class(nil, nil, EatFoodAbility)

View File

@ -0,0 +1,94 @@
local Ability = {}
local EBusyRoleState = import("EBusyRoleState")
local ERoleMoveDirection = import("ERoleMoveDirection")
local EBusyAnimationPhase = import("EBusyAnimationPhase")
local KismetSystemLibrary = import("KismetSystemLibrary")
local AbilitySystemBlueprintLibrary = import("AbilitySystemBlueprintLibrary")
local GetGameplayTag = require("GamePlay.Utils").GetGameplayTag
local item_pick_tag = "Change.LevelItem.Health"
local role_pick_cost_health_tag = "Change.Role.Health"
local role_pick_cost_hunger_tag = "Change.Role.Hunger"
local function GetSelfPickConsume(item_id)
end
function Ability:ctor()
self.target = nil
self.pick_phase = nil
self.delay_timer = nil
self.event_data = nil
end
function Ability:K2_ActivateAbilityFromEvent(EventData)
print("Pick Ability:K2_ActivateAbility")
self.pick_phase = EBusyAnimationPhase.PrepareCast
self:ProcessAbilityPhase()
self.event_data = EventData
self.target = EventData.Target
end
function Ability:K2_OnEndAbility()
local owner = self:GetOwningActorFromActorInfo()
if self.target:IsAlive() then
owner:TryActiveAbility("Ability.Role.Pick", self.event_data)
else
owner.proxy.state = EBusyRoleState.PickFinished
owner:UpdateRoleState()
end
end
function Ability:ProcessAbilityPhase()
local owner = self:GetOwningActorFromActorInfo()
local Animation = owner["RoleAnimation"]
Animation:PlayPickAnimation("Tree", Animation:GetMoveDirection(), self.pick_phase, 1.0)
if self.delay_timer ~= nil then
KismetSystemLibrary.K2_ClearTimerHandle(self, self.delay_timer)
end
if self.pick_phase == EBusyAnimationPhase.PrepareCast then
local delegate = slua.createDelegate(function()
self.pick_phase = EBusyAnimationPhase.Casting
self:ProcessAbilityPhase()
end)
self.delay_timer = KismetSystemLibrary.K2_SetTimerDelegate(delegate, 0.5, false, true, 0, 0)
elseif self.pick_phase == EBusyAnimationPhase.Casting then
self.pick_phase = EBusyAnimationPhase.PostCast
self:ProcessAbilityPhase()
self:ApplayEffect(owner.RoleAbility)
else
local delegate = slua.createDelegate(function()
self:K2_EndAbility()
end)
self.delay_timer = KismetSystemLibrary.K2_SetTimerDelegate(delegate, 0.2, false, true, 0, 0)
end
end
function Ability:ApplayEffect(asc)
local RoleUtils = require("GamePlay.Utils.RoleUtils")
local owner = self:GetOwningActorFromActorInfo()
-- 物品的采集效果
local item_asc = AbilitySystemBlueprintLibrary.GetAbilitySystemComponent(self.target)
local pick_handle = asc:MakeOutgoingSpec(
self.AbilityEffectConfigs:Get("LevelItem"), 1, asc:MakeEffectContext()
)
AbilitySystemBlueprintLibrary.AssignTagSetByCallerMagnitude(
pick_handle, GetGameplayTag(item_pick_tag), -owner.RoleConfig.PickEffect
)
-- 自己的消耗
RoleUtils.ChangeHunger(owner,-self.target.LevelItemConfig.PickHungerCost)
asc:BP_ApplyGameplayEffectSpecToTarget(pick_handle, item_asc)
end
return Class(nil, nil, Ability)

View File

@ -0,0 +1,26 @@
local Ability = {}
local GetGameplayTag = require("GamePlay.Utils").GetGameplayTag
local BlueprintGameplayTagLibrary = import("BlueprintGameplayTagLibrary")
local AbilitySystemBlueprintLibrary = import("AbilitySystemBlueprintLibrary")
function Ability:K2_ActivateAbilityFromEvent(EventData)
local tag = EventData.tag
local value = EventData.EventMagnitude
local asc = AbilitySystemBlueprintLibrary.GetAbilitySystemComponent(self.target)
if tag == "Recover.Role.Health" then
local spec_handle = asc:MakeOutgoingSpec(
self.AbilityEffectConfigs:Get("Role"), 1, asc:MakeEffectContext()
)
AbilitySystemBlueprintLibrary.AssignTagSetByCallerMagnitude(
spec_handle, GetGameplayTag("Change.Role.Health"), value
)
asc:BP_ApplyGameplayEffectSpecToSelf(spec_handle)
end
end
return Class(nil, nil, Ability)

View File

@ -0,0 +1,71 @@
local Ability = {}
local Reactive = require("Core.Reactive")
local GetGameplayTag = require("GamePlay.Utils").GetGameplayTag
local AbilitySystemBlueprintLibrary = import("AbilitySystemBlueprintLibrary")
local hunger_consume_tag_name = "Buff.RoleConsume.Hunger"
local health_consume_tag_name = "Buff.RoleConsume.Health"
function Ability:K2_ActivateAbilityFromEvent(_)
print("Role Consume Ability:K2_ActivateAbility")
if self.hunger_watcher ~= nil then
self.hunger_watcher:Destroy()
end
self.hunger_watcher = Reactive.Watcher(function() self:ConsumeWatcher() end)
end
function Ability:ConsumeWatcher()
local owner = self:GetOwningActorFromActorInfo()
local asc = owner["RoleAbility"]
if owner.LuaRoleAttribute.Hunger > 0 then
if not asc:IsGameplayCueActive(GetGameplayTag(hunger_consume_tag_name)) then
self:ApplyHungerConsume(asc)
end
if asc:IsGameplayCueActive(GetGameplayTag(health_consume_tag_name)) then
if self.health_consume_handle ~= nil then
asc:RemoveActiveGameplayEffect(self.health_consume_handle, -1)
end
self.health_consume_handle = nil
end
elseif owner.LuaRoleAttribute.Hunger <= 0 then
if not asc:IsGameplayCueActive(GetGameplayTag(health_consume_tag_name)) then
self:ApplyHealthConsume(asc)
end
if asc:IsGameplayCueActive(GetGameplayTag(hunger_consume_tag_name)) then
if self.hunger_consume_handle ~= nil then
asc:RemoveActiveGameplayEffect(self.hunger_consume_handle, -1)
end
self.hunger_consume_handle = nil
owner.LuaRoleAttribute.Hunger = 0
end
end
end
function Ability:ApplyConsumeEffect(asc, effect_tag_name, value)
local spec_handle = asc:MakeOutgoingSpec(
self.EffectConfigs:Get(GetGameplayTag(effect_tag_name)),
1, asc:MakeEffectContext()
)
AbilitySystemBlueprintLibrary.AssignTagSetByCallerMagnitude(
spec_handle, GetGameplayTag(effect_tag_name), value
)
return asc:BP_ApplyGameplayEffectSpecToSelf(spec_handle)
end
function Ability:ApplyHungerConsume(asc)
local owner = asc:GetOwner()
local consume_speed_peer_second = owner.RoleConfig.HungerConsumeSpeed
self.hunger_consume_handle = self:ApplyConsumeEffect(
asc, hunger_consume_tag_name, -consume_speed_peer_second / 10
)
end
function Ability:ApplyHealthConsume(asc)
local owner = asc:GetOwner()
local consume_speed_peer_second = owner.RoleConfig.HungerConsumeSpeed
self.health_consume_handle = self:ApplyConsumeEffect(
asc, health_consume_tag_name, -consume_speed_peer_second/10
)
end
return Class(nil, nil, Ability)

View File

@ -0,0 +1,21 @@
local Bonfire = {}
local GamePlayUtils = require("GamePlay.Utils")
local Utils = require("GamePlay.Utils")
local item_effect_health_tag = "Change.Role.Health"
local item_effect_hunger_tag = "Change.Role.Health"
function Bonfire:ctor()
end
function Bonfire:ReceiveBeginPlay()
self.Inventory:SetInventoryCapacity(20)
end
function Bonfire:StoreItem(item_id)
return self.Inventory:DepositItems(item_id, 1)
end
return Class(nil, nil, Bonfire)

View File

@ -0,0 +1,108 @@
-- 禁用不必要的if诊断警告
---@diagnostic disable: unnecessary-if
-- 导入基类模块
local PWClass = require("Core.PWClass")
--- @class StorageClass
--- @field max_capacity number 最大容量(格子总数)
--- @field cur_capacity number 当前已用容量
--- @field grids_list table[] 存储格子列表
local StorageClass = PWClass.derive("StorageClass")
--- 创建新物品格子
--- @param item_id any 物品唯一标识
--- @return table 新创建的物品格子
local function CreateGrid(item_id)
return {
item_id = item_id, -- 物品ID
cur_cnt = 0, -- 当前数量
max_cnt = 1 -- 最大堆叠数(可扩展为配置项)
}
end
--- 查找或创建可用物品格子
--- @param storage StorageClass 存储实例
--- @param item_id any 目标物品ID
--- @return table 可用格子(找不到时创建新格子)
local function FindOrCreateAvailableGrid(storage, item_id)
-- 优先查找同类型且未满的格子
for _, grid in ipairs(storage.grids_list) do
if grid.item_id == item_id and grid.cur_cnt < grid.max_cnt then
return grid
end
end
-- 无可用格子时创建新的物品类型格子
local new_grid = CreateGrid(item_id)
table.insert(storage.grids_list, new_grid)
return new_grid
end
--- 构造函数
function StorageClass:ctor()
self.max_capacity = 10 -- 默认最大容量
self.cur_capacity = 0 -- 当前使用容量
self.grids_list = {} -- 格子容器
end
--- 设置存储容量上限
--- @param capacity number 新的最大容量
function StorageClass:SetMaxCapacity(capacity)
self.max_capacity = capacity
end
--- 存储物品
--- @param item_id any 要存储的物品ID
function StorageClass:Store(item_id)
-- 容量检查
if self.cur_capacity >= self.max_capacity then
return false -- 建议返回操作结果
end
local grid = FindOrCreateAvailableGrid(self, item_id)
grid.cur_cnt = grid.cur_cnt + 1
self.cur_capacity = self.cur_capacity + 1
return true -- 建议添加返回值
end
--- 取出物品
--- @param item_id any 目标物品ID
--- @return boolean 是否成功取出
function StorageClass:Withdraw(item_id)
-- 逆序遍历提高取出效率(通常新物品在末尾)
for i = #self.grids_list, 1, -1 do
local grid = self.grids_list[i]
if grid ~= nil and grid.item_id == item_id and grid.cur_cnt > 0 then
grid.cur_cnt = grid.cur_cnt - 1
self.cur_capacity = self.cur_capacity - 1
-- 清空空格子
if grid.cur_cnt == 0 then
table.remove(self.grids_list, i)
end
return true
end
end
return false
end
function StorageClass:Visit(vistor)
for _, grid in ipairs(self.grids_list) do vistor(_, grid) end
end
--- 查询物品(满足条件的物品及数量)
--- @param query_function fun(item_id:any):boolean 物品过滤函数
--- @return table 物品ID到数量的映射表
function StorageClass:QueryItem(query_function)
local items = {}
for _, grid in ipairs(self.grids_list) do
-- 仅统计有物品且满足查询条件的格子
if grid.cur_cnt > 0 and query_function(grid.item_id) then
items[grid.item_id] = (items[grid.item_id] or 0) + grid.cur_cnt
end
end
return items
end
return StorageClass

View File

@ -0,0 +1,18 @@
local PlayerState = {}
function PlayerState:ctor()
end
function PlayerState:ReceiveBeginPlay()
print(self, "PlayerState:ReceiveBeginPlay")
end
function PlayerState:ReceiveEndPlay()
print(self, "PlayerState:ReceiveEndPlay")
end
return Class(nil, nil, PlayerState)

View File

@ -0,0 +1,68 @@
local Animation = {}
local ERoleState = import("EBusyRoleState")
local ERoleMoveDirection = import("ERoleMoveDirection")
local Reactive = require("Core.Reactive")
local direction_mapping = {
[0] = ERoleMoveDirection.Move_Right,
[1] = ERoleMoveDirection.Move_Left,
}
local function GetAnimationIndex(direction, N)
-- 使用自定义 atan2 计算角度
local angle = math.atan(direction.Y, direction.X)
-- 转换为 [0, 2π) 范围
if angle < 0 then angle = angle + 2 * math.pi end
-- 调整角度:使 Y 轴负方向 (0,-1) 成为 0°第一份的中心
local adjusted_angle = (angle + math.pi / N) % (2 * math.pi)
-- local adjusted_angle = (angle + math.pi / 2 + math.pi / N) % (2 * math.pi)
-- 计算每份的角度大小
local theta = 2 * math.pi / N
-- -- 计算所属区间的索引1-based
local index = math.floor(adjusted_angle / theta)
-- 处理边界情况adjusted_angle = 2π 时归到第 1 份)
if index > N then index = 0 end
return index
end
function Animation:ReceiveLuaBeginPlay()
self.prev_direction = nil
self.watcher = Reactive.Watcher(function() self:UpdateMoveAnimation() end)
end
function Animation:UpdateMoveAnimation()
local owner = self:GetOwner()
local movement_proxy = owner.Movement.proxy
local index = 0
if owner.proxy.state == ERoleState.Picking then
return
end
if movement_proxy.isIdle then
if self.prev_direction ~= nil then
index = GetAnimationIndex(self.prev_direction, ERoleMoveDirection.Move_All_Cnt)
end
self:SetIdleAnimation(direction_mapping[index])
else
local direction = movement_proxy.direction
if direction.X ~= 0 and direction.Y ~= 0 then
index = GetAnimationIndex(direction, ERoleMoveDirection.Move_All_Cnt)
end
self:SetMoveAnimation(direction_mapping[index])
self.prev_direction = {X = direction.X, Y = direction.Y}
end
end
function Animation:GetMoveDirection()
local index = GetAnimationIndex(self.prev_direction, ERoleMoveDirection.Move_All_Cnt)
return direction_mapping[index]
end
return Class(nil, nil, Animation)

View File

@ -0,0 +1,78 @@
local FVector = import "Vector"
local FVector2D = import "Vector2D"
local Reactive = require "Core.Reactive"
local BusyActorManagerSubSystem = import("BusyActorManagerSubSystem")
local Movement = {
proxy = {},
}
function Movement:ReceiveComponentBeginPlay()
self.proxy = Reactive.ReactiveProperty({
isIdle = true,
direction = {X = 0, Y = 0},
destination = {X = 0, Y = 0}
})
end
function Movement:ReceiveComponentTick(delta_time)
local proxy = self.proxy
if proxy.isIdle then return end
local new_pos = FVector()
local owner = self:GetOwner()
local curr_pos = owner:K2_GetActorLocation()
new_pos.Z = curr_pos.Z
local move_distance = delta_time * owner:GetSpeed()
local distance_x = proxy.destination.X - curr_pos.X
local distance_y = proxy.destination.Y - curr_pos.Y
if move_distance^2 >= distance_x^2 + distance_y^2 then
proxy.isIdle = true
new_pos.X = proxy.destination.X
new_pos.Y = proxy.destination.Y
proxy.direction = FVector2D()
else
new_pos.X = curr_pos.X + proxy.direction.X * move_distance
new_pos.Y = curr_pos.Y + proxy.direction.Y * move_distance
end
owner:K2_SetActorLocation(new_pos, true, nil, false)
end
function Movement:SetDestination(x, y)
local direction = FVector2D()
local destination = FVector2D()
local owner = self:GetOwner()
local curr_pos = owner:K2_GetActorLocation()
local delta_x = x - curr_pos.X
local delta_y = y - curr_pos.Y
-- 计算方向向量
local length = math.sqrt(delta_x ^ 2 + delta_y ^ 2)
if length > 0 then
direction.X, direction.Y = delta_x / length, delta_y / length
else
direction.X, direction.Y = 0, 0
end
destination.X , destination.Y = x, y
self.proxy.isIdle = false
self.proxy.direction = direction
self.proxy.destination = destination
end
function Movement:Stop()
self.proxy.isIdle = true
end
function Movement:BackBonfire()
local sub_system = BusyActorManagerSubSystem.Get(self)
local bonfire = sub_system:GetNearestBonfire()
local position = bonfire:K2_GetActorLocation()
self:SetDestination(position.X, position.Y)
end
return Class(nil, nil, Movement)

View File

@ -0,0 +1,15 @@
local InventoryComponent = {}
function InventoryComponent:ReceiveBeginPlay()
print(self, "InventoryComponent:ReceiveBeginPlay")
end
function InventoryComponent:InitItemGrid(grid)
grid.MaxCount = 1
grid.CurrentCount = 0
grid.Priority = 1
return grid
end
return Class(nil, nil, InventoryComponent)

View File

@ -0,0 +1,59 @@
local Movement = {}
local RoleUtils = require("GamePlay.Utils.RoleUtils")
local Vector2D = require("Utils.Vector2D")
function Movement:ctor()
self.speed = 300.0
self.accelerate = 300.0
self.rate = 0.0
self.direction = Vector2D.New()
end
function Movement:ReceiveBeginPlay()
self.rate = 0.0
self.direction = Vector2D.Normalize(self.direction)
end
function Movement:SetMoveOriginDirection(vector)
self.direction = Vector2D.Normalize(vector)
end
function Movement:SetMoveOriginSpeed(speed, accelerate)
self.speed = speed
self.accelerate = accelerate
end
function Movement:ReceiveTick(DeltaSeconds)
local owner = self:GetOwner()
local role = RoleUtils.GetRole(self)
local role_location = role:K2_GetActorLocation()
local curr_location = owner:K2_GetActorLocation()
local accelerate_vector = Vector2D.New(
role_location.X - curr_location.X,
role_location.Y - curr_location.Y
)
accelerate_vector = Vector2D.Normalize(accelerate_vector)
self.rate = self.rate + DeltaSeconds * 0.1
local velocity_vector = Vector2D.Mul(self.direction, self.speed)
local direction_vector= Vector2D.Mul(accelerate_vector, self.speed * self.rate)
self.direction = Vector2D.Normalize(Vector2D.Add(velocity_vector, direction_vector))
local new_velocity = Vector2D.Mul(self.direction, self.speed)
local new_location = Vector2D.New(
curr_location.X + new_velocity.X * DeltaSeconds,
curr_location.Y + new_velocity.Y * DeltaSeconds
)
self.speed = self.speed + self.accelerate * DeltaSeconds
owner:K2_SetActorLocation(
Vector2D.ToUnrealEngine3D(new_location, curr_location.Z),
true, nil, false
)
end
function Movement:ReceiveEndPlay()
print("Movement:ReceiveEndPlay 123")
end
return Class(nil, nil, Movement)

View File

@ -0,0 +1,112 @@
local FVector = import "Vector"
local Vector2D = require("Utils.Vector2D")
local Library = import "BusyGamePlayLibrary"
local GameplayStatics = import("GameplayStatics")
local SubSystem = {}
local function GetNearestBonfire(system, x, y)
local selected_bonfire = nil
local selected_distance = nil
for _, bonfire in ipairs(system.bonfire_list) do
local pos = bonfire:K2_GetActorLocation()
local distance = (x - pos.X) ^ 2 + (y - pos.Y) ^ 2
if selected_distance == nil or distance < selected_distance then
selected_distance = distance
selected_bonfire = bonfire
end
end
return selected_bonfire
end
function SubSystem:ctor()
self.current_role = nil
self.bonfire_list = {} -- 所有的篝火列表
end
function SubSystem:ReceiveSubSystemInitialize()
self.current_role = nil
self.bonfire_list = {}
end
function SubSystem:GetNearestBonfire()
if self.current_role then
local cur_pos = self.current_role:K2_GetActorLocation()
return GetNearestBonfire(self, cur_pos.X, cur_pos.Y)
end
return nil
end
function SubSystem:SpawnBonfire(position)
local pos = FVector()
local world = self:K2_GetWorld()
local cls = Library.GetGameClass("Bonfire")
pos.X, pos.Y, pos.Z = position.X, position.Y, 20
local bonfire = world:SpawnActor(cls, pos, nil, nil)
table.insert(self.bonfire_list, bonfire)
return bonfire
end
function SubSystem:SpawnRole(bonfire)
local role_pos = FVector()
local world = self:K2_GetWorld()
local pos = bonfire:K2_GetActorLocation()
local cls = Library.GetGameClass("BusyRole")
role_pos.X, role_pos.Y, role_pos.Z = pos.X, pos.Y, pos.Z + 10
self.current_role = world:SpawnActor(cls, role_pos, nil, nil)
if self.current_role ~= nil then
self.current_role:SetRole("Rabbit")
return self.current_role
else
return nil
end
end
function SubSystem:SpawnLevelItem(item_id)
-- 随机在角色周围生成
local distance = math.random(128, 500)
local angle = (math.random(0, 360) / 360) * 2 * 3.14;
local world = self:K2_GetWorld()
local item_position = FVector()
local center = self.current_role:K2_GetActorLocation()
local cls = import("BusyLevelItem")
item_position.Z = center.Z - 1
item_position.X = center.X + math.cos(angle) * distance
item_position.Y = center.Y + math.sin(angle) * distance
local item = world:SpawnActor(cls, item_position, nil, nil)
item:SetLevelItemID(item_id)
return center
end
function SubSystem:SpawnLevelItemReward(level_item)
assert(self.current_role ~= nil)
local world = self:K2_GetWorld()
local cls = Library.GetGameClass("LevelItemReward")
local random_angle = (math.random() - 0.5) * (math.pi / 2)
local direction = Vector2D.Normalize(self.current_role:GetMoveDirection())
local sin, cos = math.sin(random_angle), math.cos(random_angle)
-- 应用旋转矩阵
direction.X = direction.X * cos - direction.Y * sin
direction.Y = direction.X * sin + direction.Y * cos
local item_location = level_item:K2_GetActorLocation()
local reward_location = Vector2D.Add(item_location, Vector2D.Mul(direction, 200))
local item = world:SpawnActor(cls,
Vector2D.ToUnrealEngine3D(reward_location, item_location.Z),
nil, nil
)
return item
end
return Class(nil, nil, SubSystem)

View File

@ -0,0 +1,119 @@
---@class ItemGenerator 物品生成器类
local ItemGenerator = {
item_data = {},
period = 100,
item_distributions = {}
}
ItemGenerator.__index = ItemGenerator
---数组洗牌函数Fisher-Yates算法
---@param array any[] 需要打乱顺序的数组
local function ShuffleArray(array)
for i = #array, 2, -1 do -- 从后往前遍历
local j = math.random(i) -- 生成1到i的随机数
array[i], array[j] = array[j], array[i] -- 交换元素位置
end
end
---创建物品生成器实例
---@param period number 生成周期次数
---@param item_data table<number, number> 物品配置表 {物品ID = 总生成数量}
---@return ItemGenerator 物品生成器实例
local function CreateItemGenerator(period, item_data)
local self = setmetatable({}, ItemGenerator)
---@type number 存储生成周期
self.period = period
---@type table<number, number> 原始配置数据
self.item_data = item_data
---@type number 当前调用次数
self.current_call = 0
---@type table<number, number[]> 物品分布数据结构
self.item_distributions = {}
self:InitializeDistributions()
return self
end
---初始化物品分布数据
function ItemGenerator:InitializeDistributions()
-- 遍历所有物品配置
for item_id, total in pairs(self.item_data) do
-- 计算基础值和余数(保证总数 = 基础值*period + 余数)
local base = math.floor(total / self.period)
local remainder = total % self.period
-- 创建初始分布数组(全部填充基础值)
local distribution = {}
for i = 1, self.period do
distribution[i] = base
end
-- 生成索引数组并洗牌(用于随机分配余数)
local indices = {}
for i = 1, self.period do
indices[i] = i
end
ShuffleArray(indices) -- 打乱索引顺序
-- 将余数随机分配到前remainder个位置
for i = 1, remainder do
distribution[indices[i]] = distribution[indices[i]] + 1
end
-- 存储当前物品的分布数组
self.item_distributions[item_id] = distribution
end
end
---重置生成器状态当调用次数超过N时触发
function ItemGenerator:Reinitialize()
self:InitializeDistributions() -- 重新生成分布数据
self.current_call = 0 -- 重置调用计数器
end
---生成物品列表(每次调用产生一个周期的结果)
---@return number[] 包含物品ID的数组结果经过随机排序
function ItemGenerator:Generate()
-- 当超过周期次数时重置状态
if self.current_call >= self.period then
self:Reinitialize()
end
local current_step = self.current_call + 1 -- 获取当前步骤1-based
local result = {} -- 结果收集器
-- 遍历所有物品的分布数据
for item_id, distribution in pairs(self.item_distributions) do
local count = distribution[current_step] -- 获取当前步骤应生成数量
for _ = 1, count do
table.insert(result, item_id) -- 按数量添加物品ID到结果集
end
end
ShuffleArray(result) -- 打乱结果顺序保证随机性
self.current_call = self.current_call + 1 -- 增加调用计数器
return result
end
---获取当前未生成的物品剩余数量
---@return table<number, number> 返回物品ID和剩余数量的映射表
function ItemGenerator:GetRemainingItems()
local remaining = {}
local current_step = self.current_call -- 注意这里使用已调用次数0-based
-- 遍历所有物品的分布数据
for item_id, distribution in pairs(self.item_distributions) do
local total_remaining = 0
-- 计算从下一个步骤到周期结束的总数量
for step = current_step + 1, self.period do
total_remaining = total_remaining + distribution[step]
end
remaining[item_id] = total_remaining
end
return remaining
end
return CreateItemGenerator

View File

@ -0,0 +1,69 @@
local SubSystem = {}
local Reactive = require("Core.Reactive")
local Library = import("BusyGamePlayLibrary")
local GameplayStatics = import("GameplayStatics")
local BusyGamePlayLibrary = import("BusyGamePlayLibrary")
local function CreateItemGenerator(level_config_data)
local Generator = require("GamePlay.Level.BusyLevelItemGenerator")
local item_data = {}
local period = level_config_data.Period
for k, v in pairs(level_config_data.LevelItemIds) do
item_data[k] = v.CountOfPeriod
end
return Generator(period, item_data)
end
function SubSystem:ReceiveSubSystemInitialize()
local world = BusyGamePlayLibrary.K2_GetWorld(self)
self.start_time = GameplayStatics.GetTimeSeconds(world)
self.proxy = Reactive.ReactiveProperty({
current_seconds = 0
})
end
function SubSystem:ReceiveWorldBeginPlay()
local BusyActorManagerSubSystem = import("BusyActorManagerSubSystem").Get(self)
-- 读取关卡配置
local is_suc, row_data = Library.GetLevelBaseConfig("Default", nil)
assert(is_suc == true, "Can't find level base config")
self.level_base_config = row_data
-- 创建物品生成器
self.generator = CreateItemGenerator(row_data)
-- 创建初始篝火
local bonfire = BusyActorManagerSubSystem:SpawnBonfire(row_data.FirstBonfirePosition)
-- 创建角色
local role = BusyActorManagerSubSystem:SpawnRole(bonfire)
GameplayStatics.GetPlayerController(self, 0):Possess(role)
end
function SubSystem:ReceiveSubSystemTick(DeltaTime)
-- local proxy = self.proxy
-- local world = BusyGamePlayLibrary.K2_GetWorld(self)
-- local current_time = GameplayStatics.GetTimeSeconds(world)
-- local escapse_time = math.floor(current_time - self.start_time)
-- if escapse_time > proxy.current_seconds then
-- self:TrySpawnLevelItem()
-- proxy.current_seconds = escapse_time
-- print(proxy.current_seconds)
-- end
end
function SubSystem:TrySpawnLevelItem()
local BusyActorManagerSubSystem = import("BusyActorManagerSubSystem").Get(self)
local items = self.generator:Generate()
for i, item_id in pairs(items) do
BusyActorManagerSubSystem:SpawnLevelItem(item_id)
print(i, item_id)
end
end
return Class(nil, nil, SubSystem)

View File

@ -0,0 +1,125 @@
--- @class BusyLevelItem
local LevelItem = {}
local Reactive = require("Core.Reactive")
local RoleUtils = require("GamePlay.Utils.RoleUtils")
local GameplayUtils = require("GamePlay.Utils")
local KismetSystemLibrary = import("KismetSystemLibrary")
--[[
Begin Object Class=/Script/BlueprintGraph.K2Node_CallFunction Name="K2Node_CallFunction_0" ExportPath="/Script/BlueprintGraph.K2Node_CallFunction'/Game/Blueprint/Bp_BusyCharacter.Bp_BusyCharacter:EventGraph.K2Node_CallFunction_0'"
bIsPureFunc=True
FunctionReference=(MemberParent="/Script/CoreUObject.Class'/Script/Engine.KismetSystemLibrary'",MemberName="IsValid")
NodePosX=416
NodePosY=576
NodeGuid=FAEC35AE4921EA704624228C12C6567F
CustomProperties Pin (PinId=560B26AF426C21B8AC88EDB90990EDF1,PinName="self",PinFriendlyName=NSLOCTEXT("K2Node", "Target", "Target"),PinToolTip="Target\nKismet System Library Object Reference",PinType.PinCategory="object",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.Class'/Script/Engine.KismetSystemLibrary'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultObject="/Script/Engine.Default__KismetSystemLibrary",PersistentGuid=00000000000000000000000000000000,bHidden=True,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=5F3C3BC8429FA25D9B78099F2107C098,PinName="Object",PinToolTip="Object\nObject Reference",PinType.PinCategory="object",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.Class'/Script/CoreUObject.Object'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=True,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=5C7188354FCB6B61DE0AC8A14D6E3EEC,PinName="ReturnValue",PinToolTip="Return Value\nBoolean\n\nReturn true if the object is usable : non-null and not pending kill",Direction="EGPD_Output",PinType.PinCategory="bool",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="false",AutogeneratedDefaultValue="false",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
End Object
]]
function LevelItem:ReceiveBeginPlay()
self.Super:ReceiveBeginPlay()
self:SetLevelItemID(self.CurrentItemID)
self.world = self:GetWorld()
end
function LevelItem:ReceiveLevelItemSetted(Config)
self.config = Config
self.PickBar.Widget:BindLevelItem(self)
print("LevelItem:ReceiveLevelItemSetted", KismetSystemLibrary.IsValid(self))
self.attribute_watcher = Reactive.Watcher(function()
if self.LuaLevelItemAttribute.Health <= 0 then
self:GenerateDropItems()
local location = self:K2_GetActorLocation()
location.Z = -1000
self:K2_SetActorLocation(location, false, nil, false)
self:SetLifeSpan(0.3)
self.attribute_watcher:Destroy()
end
end)
end
-- function LevelItem:ReceiveDamage(value)
-- local remain = self.proxy.RemainProcess - value
-- if remain > 0 then
-- self.proxy.RemainProcess = remain
-- return true
-- else
-- self.proxy.RemainProcess = 0
-- self:SetLifeSpan(0.3)
-- return false
-- end
-- end
function LevelItem:GenerateDropItems()
local BusyActorManagerSubSystem = import("BusyActorManagerSubSystem").Get(self.world)
local role = RoleUtils.GetRole(self)
local role_location = role:K2_GetActorLocation()
local curr_location = self:K2_GetActorLocation()
local collection_config = GameplayUtils.GetItemConfigByID(self.CurrentItemID)
local generated_dict = {}
for _, config in pairs(collection_config.DropConfigs) do
local base = 0
local seed = math.random()
for _, rate in pairs(config.configs) do
local min = base
local max = base + rate.Rate
if seed >= min and seed < max then
generated_dict[config.ItemID] = rate.Count
break
end
base = max
end
end
for item_id, count in pairs(generated_dict) do
for _ = 1, count do
local reward = BusyActorManagerSubSystem:SpawnLevelItemReward(self)
reward.Movement.speed = 300.0
reward.Movement.accelerate = 600.0
reward.Movement.direction = {
X = curr_location.X - role_location.X,
Y = curr_location.Y - role_location.Y
}
reward:SetRewardID(item_id)
end
end
end
-- 接口
function LevelItem:GetPickProcess()
local process = self.LuaLevelItemAttribute.Health / self.config.PickTimeCost
print("current process", process)
return process
end
function LevelItem:GetPickCost()
return self.LevelItemConfig.PickHungerCost
end
function LevelItem:IsAlive()
return self.LuaLevelItemAttribute.Health > 0
end
function LevelItem:GetItemID()
return self.CurrentItemID
end
return Class(nil, nil, LevelItem)

View File

@ -0,0 +1,6 @@
local GameMode = {}
function GameMode:ReceiveBeginPlay()
require("Debugger.LuaPanda").start("127.0.0.1",8818)
self.Super:ReceiveBeginPlay()
end
return Class(nil, nil, GameMode)

View File

@ -0,0 +1,24 @@
local PlayerController = {}
local KismetSystemLibrary = import("KismetSystemLibrary")
function PlayerController:convertCursorToWorldPosition()
-- 将当前鼠标的位置转化为世界的位置
local FVector = import("Vector")
local WorldOrigin, WorldDirection = FVector(), FVector()
local _, MouseX, MouseY = self:GetMousePosition(nil, nil)
if self:DeprojectScreenPositionToWorld(
MouseX, MouseY, WorldOrigin, WorldDirection
)then
return WorldOrigin.X, WorldOrigin.Y
else
return nil, nil
end
end
function PlayerController:QuitGame()
KismetSystemLibrary.QuitGame(self, self, 0, false)
end
return Class(nil, nil, PlayerController)

View File

@ -0,0 +1,203 @@
local UIUtils = require("UI.Utils")
local Reactive = require("Core.Reactive")
local ERoleState = import("EBusyRoleState")
local EBusyItemEffectType = import("EBusyItemEffectType")
local GameplayStatics = import ("GameplayStatics")
local GetGameplayTag = require("GamePlay.Utils").GetGameplayTag
local BusyActorManagerSubSystem = import("BusyActorManagerSubSystem")
local GamePlayUtils = require("GamePlay.Utils")
local KismetMathLibrary = import("KismetMathLibrary")
local LevelItemRewardClass = import("LevelItemReward")
--- @class BusyRole
local Role={
movement_watcher = nil,
interacte_item = nil
}
local PickAbilityTag = "Ability.Role.Pick"
local RecoverAbilityTag = "Ability.Role.Recover"
local ConsumeAbilityTag = "Ability.Role.AttributeConsume"
local RollAbilityTag = "Ability.Role.Roll"
-- 私有函数
-- 采集相关
--- @param role BusyRole
--- @param pick_targer BusyLevelItem
local function ResetPickWatcher(role, pick_targer)
if role.pick_watcher ~= nil then
role.pick_watcher:Destroy()
end
role.pick_watcher = Reactive.Watcher(function()
if not pick_targer:IsAlive() then
role.pick_watcher:Destroy()
role.pick_watcher = nil
role.carried_item_id = pick_targer:GetItemID()
end
end)
end
--- 存储正在搬运的物品
local function StoreCarriedItem(role)
-- if not role.carried_item_id then return end
-- local sub_system = BusyActorManagerSubSystem.Get(role)
-- local bonfire = sub_system:GetNearestBonfire()
-- if bonfire:StoreItem(role.carried_item_id) then
-- role.carried_item_id = nil
-- end
end
function Role:ctor()
self.pick_timer = nil
self.carried_item_id = nil
self.time_limited_tags = {}
self.pick_watcher = nil -- 监听正在采集物品的状态
self.proxy = Reactive.ReactiveProperty({
state = ERoleState.BonfireIdle,
is_alive = true,
})
end
function Role:ReceiveBeginPlay()
self.bCanEverTick = false
self.Inventory:DepositItems(200001, 2)
self.movement_watcher = Reactive.Watcher(function()
self:UpdateRoleState()
end)
end
function Role:ReceiveEndPlay()
print(self, "Role:ReceiveEndPlay")
end
function Role:ReceiveSetRole(_)
self:TryActiveAbility(ConsumeAbilityTag, nil)
UIUtils.ShowWidget(self, "RoleState", {role=self})
end
function Role:UpdateRoleState()
-- 在auto run中访问Reactive Property的值会导致多次调用有性能隐患
local role_state = Reactive.RawGet(self.proxy, "state")
local move_proxy = self.Movement.proxy
if move_proxy.isIdle then
if role_state == ERoleState.Searching or role_state == ERoleState.PickFinished then
self.proxy.state = ERoleState.BackBonfire
self.Movement:BackBonfire()
elseif role_state == ERoleState.BackBonfire then
-- StoreCarriedItem(self)
self.proxy.state = ERoleState.BonfireIdle
end
else
if role_state == ERoleState.BonfireIdle then
self.proxy.state = ERoleState.Searching
end
end
print("old", role_state, "new", Reactive.RawGet(self.proxy, "state"), move_proxy.isIdle)
end
function Role:StartPick(level_item)
local GameplayEventData = import("GameplayEventData")
local EventData = GameplayEventData()
EventData.Instigator = self
EventData.Target = level_item
self.proxy.state = ERoleState.Picking
self:TryActiveAbility(PickAbilityTag, EventData)
-- ResetPickWatcher(self, level_item)
end
function Role:OnTouch(_)
local role_proxy = self.proxy
if role_proxy.state == ERoleState.BonfireIdle then
local pc = GameplayStatics.GetPlayerController(self:GetWorld(), 0)
local world_x, world_y = pc:convertCursorToWorldPosition()
role_proxy.state = ERoleState.Searching
self.Movement:SetDestination(world_x, world_y)
else
print("nothing")
end
end
function Role:OnOverlapBegin(OverlappedComp, OtherActor, OtherComp, OtherBodyIndex, bFromSweep, SweepResult)
if KismetMathLibrary.ClassIsChildOf(OtherActor:GetClass(), LevelItemRewardClass) then
self.Inventory:DepositItems(OtherActor.RewardID, 1)
OtherActor:ConditionalBeginDestroy()
else
if self.proxy.state ~= ERoleState.Searching then return end
self.proxy.state = ERoleState.Picking
self.Movement:Stop()
self:StartPick(OtherActor)
end
end
--- 使用翻滚技能
function Role:UseRollSkill()
self:TryActiveAbility(RollAbilityTag, nil)
end
-- 接口
function Role:GetPickEffect()
return self.RoleConfig.PickEffect
end
function Role:GetHealthPercent()
local current_health = self.LuaRoleAttribute.Health or 0
return current_health / self.RoleConfig.Health
end
function Role:GetHungerPercent()
return self.LuaRoleAttribute.Hunger / self.RoleConfig.Hunger
end
function Role:GetHungerConsumeSpeed()
if self.time_limited_tags["SkillRole"] ~= nil then
return self.RoleConfig.HungerConsumeSpeed * 8
else
return self.RoleConfig.HungerConsumeSpeed
end
end
function Role:GetSpeed()
return self.LuaRoleAttribute.MoveSpeed
end
function Role:GetMoveDirection()
return self.Movement.proxy.direction
end
--- 返回即将消耗的饥饿值和生命值
function Role:CalcRealChangeByHunger(value)
local config = self.RoleConfig
local fixed_change, extra_health_need = 0, 0
local cost_rate = config.HealthConsumeSpeed / config.HungerConsumeSpeed
local remain_hunger = self.LuaRoleAttribute.Hunger + value
if remain_hunger < 0 then
fixed_change = remain_hunger
extra_health_need = remain_hunger * cost_rate
elseif remain_hunger > self.RoleConfig.Hunger then
fixed_change = remain_hunger - self.RoleConfig.Hunger
end
return value - fixed_change, self:CalcRealChangeByHealth(extra_health_need)
end
function Role:CalcRealChangeByHealth(value)
if value == 0 then return 0 end
local fixed_change = 0
local remain_health = self.LuaRoleAttribute.Health + value
if remain_health < 0 then
fixed_change = remain_health
elseif remain_health > self.RoleConfig.Health then
fixed_change = remain_health - self.RoleConfig.Health
end
return value - fixed_change
end
return Class(nil, nil, Role)

View File

@ -0,0 +1,37 @@
local _M = {}
local GameplayTag = import("GameplayTag")
local BusyGamePlayLibrary = import("BusyGamePlayLibrary")
local function GetConfig(func_name, config_id)
local state, config = BusyGamePlayLibrary[func_name](config_id, nil)
if state == true then
return config
else
return nil
end
end
function _M.GetItemConfigByID(item_id)
return GetConfig("GetLevelItemConfig", item_id)
end
function _M.GetItemResourceConfig(item_id)
return GetConfig("GetItemResourceConfig", item_id)
end
function _M.GetItemDescConfig(item_id)
return GetConfig("GetLevelItemDescription", item_id)
end
function _M.GetRoleConfigByID(role_id)
return GetConfig("GetRoleConfig", role_id)
end
function _M.GetGameplayTag(name)
local tag = GameplayTag(name)
tag.TagName = name
return tag
end
return _M

View File

@ -0,0 +1,29 @@
local BuildUtils = {}
local BusyActorManagerSubSystem = import("BusyActorManagerSubSystem")
local function BuildBonfire(wco)
local sub_system = BusyActorManagerSubSystem.Get(wco)
local role = sub_system.current_role
local bonfire = sub_system:GetNearestBonfire()
local role_pos = role:K2_GetActorLocation()
local bonfire_pos = bonfire:K2_GetActorLocation()
local distance = (role_pos.X - bonfire_pos.X)^2 + (role_pos.Y - bonfire_pos.Y)^2
if distance >= 90000 then
sub_system:SpawnBonfire(role_pos)
return true
end
return false
end
local build_mapping = {
[200001] = BuildBonfire
}
function BuildUtils.Build(wco, item_id)
local build_function = build_mapping[item_id]
return build_function and build_function(wco) or false
end
return BuildUtils

View File

@ -0,0 +1,58 @@
local _M = {}
local GameplayEventData = import("GameplayEventData")
local GetGameplayTag = require("GamePlay.Utils").GetGameplayTag
local BusyGamePlayLibrary = import("BusyGamePlayLibrary")
local BusyActorManagerSubSystem = import("BusyActorManagerSubSystem")
local AbilitySystemBlueprintLibrary = import("AbilitySystemBlueprintLibrary")
function _M.ChangeHealth(role, value)
local asc = AbilitySystemBlueprintLibrary.GetAbilitySystemComponent(role)
local ge = asc.AbilityEffectConfigs:Get("ChangeHealth")
local cost_handle = asc:MakeOutgoingSpec(ge, 1, asc:MakeEffectContext())
AbilitySystemBlueprintLibrary.AssignTagSetByCallerMagnitude(
cost_handle, GetGameplayTag("Change.Role.Health"),
role:CalcRealChangeByHealth(value)
)
asc:BP_ApplyGameplayEffectSpecToSelf(cost_handle)
end
function _M.ChangeHunger(role, value)
local asc = AbilitySystemBlueprintLibrary.GetAbilitySystemComponent(role)
local ge = asc.AbilityEffectConfigs:Get("ChangeHunger")
local hunger_cost, health_cost = role:CalcRealChangeByHunger(value)
local cost_handle = asc:MakeOutgoingSpec(ge, 1, asc:MakeEffectContext())
AbilitySystemBlueprintLibrary.AssignTagSetByCallerMagnitude(
cost_handle, GetGameplayTag("Change.Role.Health"), health_cost
)
AbilitySystemBlueprintLibrary.AssignTagSetByCallerMagnitude(
cost_handle, GetGameplayTag("Change.Role.Hunger"), hunger_cost
)
asc:BP_ApplyGameplayEffectSpecToSelf(cost_handle)
end
function _M.GetRole(wco)
local mgr = BusyActorManagerSubSystem.Get(wco)
return mgr.current_role
end
function _M.EatFood(wco, item_id)
local role = _M.GetRole(wco)
local EventData = GameplayEventData()
EventData.EventMagnitude = item_id
role:TryActiveAbility("Ability.Role.EatFood", EventData)
end
function _M.GetRoleConfig(role_id)
local data_table = BusyGamePlayLibrary.GetGameDataTable("RoleConfig")
local row_data = data_table:FindRow(role_id, "error in here")
print(row_data)
end
return _M

View File

@ -0,0 +1,130 @@
local BagGridWidget = {}
local UIUtils = require("UI.Utils")
local GamePlayUtils = require("GamePlay.Utils")
local RoleUtils = require("GamePlay.Utils.RoleUtils")
local BuildUtils = require("GamePlay.Utils.BuildUtils")
local ESlateVisibility = import("ESlateVisibility")
local BlueprintGameplayTagLibrary = import("BlueprintGameplayTagLibrary")
local function SetIsSelected(check_box, is_selected)
if is_selected == nil then
check_box:SetVisibility(ESlateVisibility.Collapsed)
else
check_box:SetVisibility(ESlateVisibility.Visible)
check_box:SetIsChecked(is_selected)
end
end
local function SetItemAndCnt(entry, item_id, item_cnt)
if item_id ~= nil then
entry.Icon:SetVisibility(ESlateVisibility.SelfHitTestInvisible)
local config = GamePlayUtils.GetItemDescConfig(item_id)
local x = slua.loadObject(config.IconResource:ToString())
entry.Icon:SetBrushResourceObject(x)
else
entry.Icon:SetVisibility(ESlateVisibility.Collapsed)
end
item_cnt = item_cnt or 0
if item_cnt > 0 then
entry.Icon:SetVisibility(ESlateVisibility.SelfHitTestInvisible)
if item_cnt > 1 then
entry.TxtCnt:SetVisibility(ESlateVisibility.SelfHitTestInvisible)
else
entry.TxtCnt:SetVisibility(ESlateVisibility.Collapsed)
end
else
entry.TxtCnt:SetVisibility(ESlateVisibility.Collapsed)
entry.Icon:SetVisibility(ESlateVisibility.Collapsed)
end
entry.item_id = item_id
end
local function GetItemMenuContent(item, item_id)
local config = GamePlayUtils.GetItemConfigByID(item_id)
if config == nil then return {} end
local contents = {}
local consume_function = function()
local inventory = item.inventory
inventory:ConsumeItems(item.index, 1)
local grid = inventory:GetGridWithIndex(item.index)
item:SetData(grid)
end
local is_food = BlueprintGameplayTagLibrary.HasTag(
config.TypeTagContainer,
GamePlayUtils.GetGameplayTag("GameItem.Food"),
false
)
local is_building = BlueprintGameplayTagLibrary.HasTag(
config.TypeTagContainer,
GamePlayUtils.GetGameplayTag("GameItem.Building"),
false
)
if is_food then
table.insert(contents, {"使用", function()
consume_function()
RoleUtils.EatFood(item, item_id)
end})
end
if is_building then
table.insert(contents, {"建造", function()
if BuildUtils.Build(item, item_id) == true then
consume_function()
end
-- RoleUtils.EatFood(item, item_id)
end})
end
table.insert(contents, {"丢弃", function()
consume_function()
end})
return contents
end
function BagGridWidget:OnInitialized()
self.BtnMain.OnClicked:Add(function() self:OnGridClicked() end)
self.CheckBox.OnCheckStateChanged:Add(function(is_selected)
if self.item == nil then return end
self.item.selected = is_selected
end)
end
function BagGridWidget:SetData(grid)
if grid == nil then grid = {} end
SetIsSelected(self.CheckBox, grid.selected)
SetItemAndCnt(self, grid.ItemID, grid.CurrentCount)
end
function BagGridWidget:SetInventoryInfo(inventory, index)
self.index = index
self.inventory = inventory
end
function BagGridWidget:OnDestroy()
end
function BagGridWidget:OnGridClicked()
if not self.item_id then return end
local FVector2D = import("Vector2D")
local SlateBlueprintLibrary = import("SlateBlueprintLibrary")
local geometry = self:GetCachedGeometry()
local size = SlateBlueprintLibrary.GetLocalSize(geometry)
local center = FVector2D()
center.X, center.Y = size.X * 0.5, size.Y * 0.5
local _, viewport_pos = SlateBlueprintLibrary.LocalToViewport(self, geometry, center, nil, nil)
UIUtils.ShowWidget(self, "MenuPanel", {
viewport_pos,
GetItemMenuContent(self, self.item_id)
})
end
return Class(nil, nil, BagGridWidget)

View File

@ -0,0 +1,57 @@
local BagPanel = {}
local UIUtils = require("UI.Utils")
local GameplayStatics = import("GameplayStatics")
local BusyActorManagerSubSystem = import("BusyActorManagerSubSystem")
function BagPanel:ctor()
self.widgets = {}
end
function BagPanel:OnInitialized()
print("BagPanel:OnInitialized")
self.BtnClose.OnClicked:Add(function() self:Close() end)
end
function BagPanel:Construct()
self["WBP_TableSwitcher"]:SetSelectedSwitcher("RoleInventory")
end
function BagPanel:GetInventory(name)
if name == "Bonfire" then
local sub_system = BusyActorManagerSubSystem.Get(self)
local bonfire = sub_system:GetNearestBonfire()
return bonfire.Inventory
end
end
function BagPanel:Close()
GameplayStatics.SetGamePaused(self, false)
UIUtils.CloseWidget(self)
end
function BagPanel:Refresh()
GameplayStatics.SetGamePaused(self, true)
-- self:SetVisibility(ESlateVisibility.Visible)
-- local sub_system = BusyActorManagerSubSystem.Get(self)
-- local bonfire = sub_system:GetNearestBonfire()
-- bonfire.Inventory:ForEach(slua.createDelegate(function(idx, grid)
-- local widget = self.widgets[idx+1]
-- widget:SetData(grid)
-- widget:SetInventoryInfo(bonfire.Inventory, idx)
-- end))
-- local drop_visible = ESlateVisibility.Collapsed
-- self.Overlay_Confim:SetVisibility(drop_visible)
end
function BagPanel:Destruct()
end
function BagPanel:OnDestroy()
end
return Class(nil, nil, BagPanel)

View File

@ -0,0 +1,58 @@
local InventoryArea = {}
local RoleUtils = require("GamePlay.Utils.RoleUtils")
local GameplayStatics = import("GameplayStatics")
local WidgetBlueprintLibrary = import("WidgetBlueprintLibrary")
local BusyActorManagerSubSystem = import("BusyActorManagerSubSystem")
function InventoryArea:ctor()
self.widgets = {}
end
function InventoryArea:PreConstruct(bIsDesignTime)
if not bIsDesignTime then return end
end
function InventoryArea:OnInitialized()
self:InitInventoryLayout(self.RowGridCnt, self.ColGridCnt, self.GridClass)
end
function InventoryArea:InitInventoryLayout(row_cnt, col_cnt, cls)
self.widgets = {}
local pc = GameplayStatics.GetPlayerController(self, 0)
for i=1, row_cnt do
for j=1, col_cnt do
local widget = WidgetBlueprintLibrary.Create(self, cls, pc)
self.GridPanel:AddChildToGrid(widget, i-1, j-1)
widget:SetData(nil)
table.insert(self.widgets, widget)
end
end
end
function InventoryArea:OnSwitchIn(inventory_name)
self:SetVisible(true)
local inventory = nil
if inventory_name == "BonfireInventory" then
local sub_system = BusyActorManagerSubSystem.Get(self)
local bonfire = sub_system:GetNearestBonfire()
inventory = bonfire.Inventory
elseif inventory_name == "RoleInventory" then
local role = RoleUtils.GetRole(self)
inventory = role.Inventory
end
if inventory == nil then return end
inventory:ForEach(slua.createDelegate(function(idx, grid)
local widget = self.widgets[idx+1]
widget:SetData(grid)
widget:SetInventoryInfo(inventory, idx)
end))
end
function InventoryArea:OnSwitchOut(inventory_name)
self:SetVisible(false)
end
return Class(nil, nil, InventoryArea)

View File

@ -0,0 +1,54 @@
local PopupMenuItem = {}
local UIUtils = require("UI.Utils")
function PopupMenuItem:ctor()
self.on_click_callback = nil
end
function PopupMenuItem:PreConstruct(IsDesignTime)
if not IsDesignTime then return end
self.TxtMenuName:SetText(self.MenuText)
end
function PopupMenuItem:OnInitialized()
self:BindEvent("OnClicked", function() self:OnMenuItemClicked() end)
self:BindEvent("OnHovered", function() self:OnHoverStateChange(true) end)
self:BindEvent("OnUnhovered", function() self:OnHoverStateChange(false) end)
end
function PopupMenuItem:Construct()
self.TxtMenuName:SetText(self.MenuText)
end
function PopupMenuItem:OnDestroy()
print(self, "PopupMenuItem:OnDestroy")
end
function PopupMenuItem:SetContent(name, callback)
self.TxtMenuName:SetText(name)
self.on_click_callback = callback
print(self.TxtMenuName:GetText())
end
function PopupMenuItem:BindEvent(event_name, callback)
self.BtnMain[event_name]:Add(callback)
end
function PopupMenuItem:OnHoverStateChange(is_hoverd)
if is_hoverd then
self.ImgBackground.Brush.TintColor = self.ItemHoverdColor
else
self.ImgBackground.Brush.TintColor = self.ItemNormalColor
end
end
function PopupMenuItem:OnMenuItemClicked()
print("OnMenuItemClicked")
if self.on_click_callback ~= nil then
self.on_click_callback()
end
UIUtils.HideWidgetByName(self, "MenuPanel")
end
return Class(nil, nil, PopupMenuItem)

View File

@ -0,0 +1,81 @@
local PopupMenuPanel = {}
local UIUtils = require("UI.Utils")
local GameplayStatics = import("GameplayStatics")
local WidgetBlueprintLibrary = import("WidgetBlueprintLibrary")
local KismetSystemLibrary = import("KismetSystemLibrary")
local function delay_adjust_content(menu_panel)
local post_callback = function()
local timer = KismetSystemLibrary.K2_SetTimerForNextTickDelegate(
slua.createDelegate(function ()
menu_panel:AdjustMenuContentSize()
menu_panel.adjust_content_timer = nil
end)
)
menu_panel.adjust_content_timer = timer
end
local timer = KismetSystemLibrary.K2_SetTimerForNextTickDelegate(
slua.createDelegate(function () post_callback() end)
)
menu_panel.adjust_content_timer = timer
end
local function CreateMenuItem(panel)
local pc = GameplayStatics.GetPlayerController(panel, 0)
local widget = WidgetBlueprintLibrary.Create(panel, panel.MenuItemClass, pc)
return widget
end
function PopupMenuPanel:ctor()
self.adjust_content_timer = nil
end
function PopupMenuPanel:OnInitialized()
self.BtnBackground.OnClicked:Add(function()
UIUtils.CloseWidget(self)
end)
end
function PopupMenuPanel:OnDestroy()
print(self, "PopupMenuPanel:OnDestroy")
end
function PopupMenuPanel:PreConstruct(IsDesignTime)
if not IsDesignTime then return end
delay_adjust_content(self)
end
function PopupMenuPanel:Construct()
delay_adjust_content(self)
end
function PopupMenuPanel:AdjustMenuContentSize()
local all_child_height = 0
for _, child in pairs(self.MenuItemContainer:GetAllChildren()) do
local design_size = child:GetDesiredSize()
all_child_height = all_child_height + design_size.Y
end
self.ImgBackground.Brush.ImageSize.Y = all_child_height + 20
end
function PopupMenuPanel:Refresh(args)
self.MainOverlay.Slot:SetPosition(args[1])
self:SetMenuContents(args[2])
end
function PopupMenuPanel:SetMenuContents(contents)
self.MenuItemContainer:ClearChildren()
for _, content in pairs(contents) do
local widget = CreateMenuItem(self)
self.MenuItemContainer:AddChildToVerticalBox(widget)
widget:SetContent(content[1], content[2])
end
self:AdjustMenuContentSize()
end
return Class(nil, nil,PopupMenuPanel)

View File

@ -0,0 +1,28 @@
local SwitcherWidget = {}
local ESlateVisibility = import("ESlateVisibility")
function SwitcherWidget:Construct()
self:Reset()
self["ImageNormal"]:SetVisibility(ESlateVisibility.SelfHitTestInvisible)
end
function SwitcherWidget:Reset()
self["ImageHovered"]:SetVisibility(ESlateVisibility.Collapsed)
self["ImageSelected"]:SetVisibility(ESlateVisibility.Collapsed)
self["ImageNormal"]:SetVisibility(ESlateVisibility.Collapsed)
end
function SwitcherWidget:OnWidgetStateChange(bIsWidgetHovered, bIsWidgetSelected)
self:Reset()
if bIsWidgetSelected then
self["ImageSelected"]:SetVisibility(ESlateVisibility.SelfHitTestInvisible)
else
if bIsWidgetHovered then
self["ImageHovered"]:SetVisibility(ESlateVisibility.SelfHitTestInvisible)
else
self["ImageNormal"]:SetVisibility(ESlateVisibility.SelfHitTestInvisible)
end
end
end
return Class(nil, nil, SwitcherWidget)

View File

@ -0,0 +1,77 @@
local Hud = {}
local ESlateVisibility = import("ESlateVisibility")
local WidgetBlueprintLibrary = import("WidgetBlueprintLibrary")
local GameplayStatics = import("GameplayStatics")
local BusyActorManagerSubSystem = import("BusyActorManagerSubSystem")
local function ProcessSuspendShowRequests(hud)
for _, request in pairs(hud.suspend_show_requests) do
hud:CreateAndShowWidget(request[1], request[2])
end
end
function Hud:ctor()
self.layer = nil
self.suspend_show_requests = {}
self.widget_pool = {}
end
function Hud:ReceiveBeginPlay()
print("Hud:ReceiveBeginPlay")
if self.layer == nil then
self.layer = self:GetOrCreateWidget("UILayer")
end
if self.layer ~= nil then
self.layer:SetVisibility(ESlateVisibility.SelfHitTestInvisible)
self.layer:AddToViewport(0)
self:CreateAndShowWidget("MainUI", {})
ProcessSuspendShowRequests(self)
end
end
function Hud:ReceiveEndPlay()
print("Hud:ReceiveEndPlay")
end
function Hud:GetOrCreateWidget(widget_name)
local exist_widget = self.widget_pool[widget_name]
if exist_widget ~= nil and exist_widget.bSingletonInstance then
return exist_widget
end
local cls = self.UIClassMapping:Get(widget_name)
local pc = GameplayStatics.GetPlayerController(self, 0)
local widget = WidgetBlueprintLibrary.Create(self, cls, pc)
self.widget_pool[widget_name] = widget
return widget -- TODO 这里如果非单例widget会被覆盖
end
function Hud:GetFirstCachedWidget(widget_name)
return self.widget_pool[widget_name]
end
function Hud:CreateAndShowWidget(widget_name, args)
if not self.layer then
table.insert(self.suspend_show_requests, {widget_name, args})
return
end
local widget = self:GetOrCreateWidget(widget_name)
if not widget then return end
self.layer:ShowWidget(widget, args)
end
function Hud:CloseWidget(widget)
self.layer:CloseWidget(widget)
end
function Hud:HideWidgetByName(widget_name)
self.layer:HideWidget(self.widget_pool[widget_name])
end
return Class(nil, nil, Hud)

32
Content/Lua/UI/MainUI.lua Normal file
View File

@ -0,0 +1,32 @@
local MainUI = {}
local UIUtils = require("UI.Utils")
-- local GameplayStatics = import("GameplayStatics")
function MainUI:OnInitialized()
print("OnInitialized MainUI")
self.BtnSetting.OnClicked:Add(function() self:OpenSettingPanel() end)
self.BtnBag.OnClicked:Add(function() self:OpenBagPanel() end)
self.BtnRoll.OnClicked:Add(function() self:OnUseRoll() end)
end
function MainUI:OnDestroy()
end
function MainUI:OpenSettingPanel()
-- GameplayStatics.SetGamePaused(self, true)
UIUtils.ShowWidget(self, "SettingMenu", {})
end
function MainUI:OpenBagPanel()
UIUtils.ShowWidget(self, "BagPanel", {})
end
function MainUI:OnUseRoll()
local BusyActorManagerSubSystem = import("BusyActorManagerSubSystem")
local mgr = BusyActorManagerSubSystem.Get(self)
local role = mgr.current_role
role:UseRollSkill()
end
return Class(nil, nil, MainUI)

View File

@ -0,0 +1,68 @@
local PickBar = {}
local Reactive = require("Core.Reactive")
local ESlateVisibility = import("ESlateVisibility")
local function ResetWatcher(Bar)
if Bar.watcher then
Bar.watcher:Destroy()
Bar.watcher = nil
end
end
function PickBar:ctor()
self.watcher = nil
self.level_item = nil
end
function PickBar:OnInitialized()
self.ProcessBar:SetVisibility(ESlateVisibility.Collapsed)
print("PickBar:OnInitialized")
end
function PickBar:BindLevelItem(LevelItem)
ResetWatcher(self)
self.level_item = LevelItem
-- self.watcher = Reactive.Watcher(function()
-- self:UpdateState()
-- end)
self.watcher = Reactive.Watcher(function()
local process = LevelItem:GetPickProcess()
if process < 1.0 then
self.ProcessBar:SetPercent(process)
self.ProcessBar:SetVisibility(ESlateVisibility.SelfHitTestInvisible)
else
self.ProcessBar:SetVisibility(ESlateVisibility.Collapsed)
self.ProcessBar:SetPercent(1.0)
end
end)
end
-- function PickBar:UpdateState()
-- local process = self.level_item:GetPickProcess()
-- if process < 1.0 then
-- self.ProcessBar:SetVisibility(ESlateVisibility.SelfHitTestInvisible)
-- self.ProcessBar:SetPercent(process)
-- else
-- self.ProcessBar:SetVisibility(ESlateVisibility.Collapsed)
-- self.ProcessBar:SetPercent(1.0)
-- end
-- end
function PickBar:OnDestroy()
ResetWatcher(self)
end
function PickBar:Construct()
print("PickBar:Construct")
end
function PickBar:Destruct()
print("PickBar:Destruct")
end
return Class(nil, nil, PickBar)

View File

@ -0,0 +1,38 @@
local Widget = {}
local Reactive = require("Core.Reactive")
local function ResetWatcher(widget)
if widget.health_watcher then
widget.health_watcher:Destroy()
widget.health_watcher = nil
end
if widget.hunger_watcher then
widget.hunger_watcher:Destroy()
widget.hunger_watcher = nil
end
end
function Widget:OnDestroy()
end
function Widget:Refresh(args)
local role = args.role
if role == nil then return end
self:BindRole(role)
end
function Widget:BindRole(role)
ResetWatcher(self)
self.health_watcher = Reactive.Watcher(function()
self.HealthBar:SetPercent(role:GetHealthPercent())
end)
self.hunger_watcher = Reactive.Watcher(function()
self.HungerBar:SetPercent(role:GetHungerPercent())
end)
end
return Class(nil, nil, Widget)

View File

@ -0,0 +1,28 @@
local SettingPanel = {}
local UIUtils = require("UI.Utils")
local GameplayStatics = import("GameplayStatics")
function SettingPanel:OnInitialized()
self.BtnContinue.OnClicked:Add(function() self:BackToGame() end)
self.BtnExitLevel.OnClicked:Add(function() end)
self.BtnExitGame.OnClicked:Add(function() self:ExitGame() end)
self.BtnClose.OnClicked:Add(function() self:BackToGame() end)
end
function SettingPanel:OnDestroy()
end
function SettingPanel:BackToGame()
GameplayStatics.SetGamePaused(self, false)
UIUtils.CloseWidget(self)
end
function SettingPanel:ExitGame()
local pc = GameplayStatics.GetPlayerController(self, 0)
pc:QuitGame()
end
return Class(nil, nil, SettingPanel)

View File

@ -0,0 +1,41 @@
local UILayerManager = {}
local EWidgetLayoutType = import("EWidgetLayoutType")
function UILayerManager:ctor()
self.layer_mapping = {}
end
function UILayerManager:OnInitialized()
self.layer_mapping = {
[EWidgetLayoutType.MainLayer] = self.MainLayer,
[EWidgetLayoutType.PopupLayer] = self.PopupLayer,
[EWidgetLayoutType.FloatLayer] = self.FloatLayer,
[EWidgetLayoutType.TopLayer] = self.TopLayer,
}
end
function UILayerManager:ShowWidget(widget, args)
local layer = self.layer_mapping[widget.LayoutType]
if layer == nil then
return
end
layer:AddChild(widget)
if widget.Refresh then
widget:Refresh(args)
end
widget.Slot:SetVerticalAlignment(0)
widget.Slot:SetHorizontalAlignment(0)
widget:SetVisible(true)
end
function UILayerManager:HideWidget(widget)
widget:SetVisible(false)
end
function UILayerManager:CloseWidget(widget)
widget:SetVisible(false)
end
return Class(nil, nil, UILayerManager)

35
Content/Lua/UI/Utils.lua Normal file
View File

@ -0,0 +1,35 @@
local _M = {}
local GameplayStatics = import("GameplayStatics")
function _M.GetGameUIHud(wco)
local pc = GameplayStatics.GetPlayerController(wco, 0)
if pc == nil then return nil end
return pc:GetHud()
end
function _M.ShowWidget(wco, widget_name, args)
local hud = _M.GetGameUIHud(wco)
if hud == nil then return end
hud:CreateAndShowWidget(widget_name, args)
end
function _M.GetShowedWidget(wco, widget_name)
local hud = _M.GetGameUIHud(wco)
if hud == nil then return end
hud:GetFirstCachedWidget(widget_name)
end
function _M.HideWidgetByName(wco, widget_name)
local hud = _M.GetGameUIHud(wco)
if hud == nil then return end
hud:HideWidgetByName(widget_name)
end
function _M.CloseWidget(widget)
local hud = _M.GetGameUIHud(widget)
if hud == nil then return end
hud:CloseWidget(widget)
end
return _M

View File

@ -0,0 +1,50 @@
local Vector2D = {}
function Vector2D.New(x, y)
return {X = x or 0, Y = y or 0}
end
function Vector2D.Add(a, b)
return Vector2D.New(a.X + b.X, a.Y + b.Y)
end
function Vector2D.Sub(a, b)
return Vector2D.New(a.X - b.X, a.Y - b.Y)
end
function Vector2D.Mul(v, s)
return Vector2D.New(v.X * s, v.Y * s)
end
function Vector2D.Div(v, s)
return Vector2D.New(v.X / s, v.Y / s)
end
function Vector2D.Magnitude(v)
return math.sqrt(v.X * v.X + v.Y * v.Y)
end
function Vector2D.Normalize(v)
local mag = Vector2D.Magnitude(v)
return mag > 0 and Vector2D.Div(v, mag) or Vector2D.New()
end
function Vector2D.Dot(a, b)
return a.X * b.X + a.Y * b.Y
end
-- 2D 叉积(返回标量)
function Vector2D.Cross(a, b)
return a.X * b.Y - a.Y * b.X
end
function Vector2D.ToUnrealEngine3D(vector, z)
local FVector = import("Vector")
local new_vector = FVector()
new_vector.Z = z
new_vector.X = vector.X
new_vector.Y = vector.Y
return new_vector
end
return Vector2D