sync with FWK
parent
ae2a4208f0
commit
55d397ff70
2
_mirror
2
_mirror
|
@ -1 +1 @@
|
|||
Subproject commit be43196a3d33412a5dbad92983af7e149b1b066d
|
||||
Subproject commit 18db6da2d1c9b1dbf954be2692f289ca54ec81d8
|
23
bind/v4k.lua
23
bind/v4k.lua
|
@ -2431,6 +2431,27 @@ struct profile_t { double stat; int32_t cost, avg; };
|
|||
typedef struct { map base; struct { pair p; char * key; struct profile_t val; } tmp, *ptr; struct profile_t* tmpval; int (*typed_cmp)(char *, char *); uint64_t (*typed_hash)(char *); } * profiler_t;
|
||||
extern profiler_t profiler;
|
||||
extern int profiler_enabled;
|
||||
typedef struct reflected_t {
|
||||
unsigned id, objtype;
|
||||
unsigned sz;
|
||||
const char *name;
|
||||
const char *info;
|
||||
void *addr;
|
||||
unsigned parent;
|
||||
const char *type;
|
||||
} reflected_t;
|
||||
unsigned enum_find(const char *E);
|
||||
void * function_find(const char *F);
|
||||
reflected_t member_find(const char *T, const char *M);
|
||||
void * member_findptr(void *obj, const char *T, const char *M);
|
||||
reflected_t* members_find(const char *T);
|
||||
void type_inscribe(const char *TY,unsigned TYid,unsigned TYsz,const char *infos);
|
||||
void enum_inscribe(const char *E,unsigned Eid,unsigned Eval,const char *infos);
|
||||
void struct_inscribe(const char *T,unsigned Tid,unsigned Tsz,unsigned OBJTYPEid, const char *infos);
|
||||
void member_inscribe(unsigned Tid, const char *M,unsigned Mid,unsigned Msz, const char *infos, const char *type);
|
||||
void function_inscribe(const char *F,unsigned Fid,void *func,const char *infos);
|
||||
void reflected_printf(reflected_t *r);
|
||||
void reflected_printf_all();
|
||||
typedef unsigned handle;
|
||||
unsigned rgba( uint8_t r, uint8_t g, uint8_t b, uint8_t a );
|
||||
unsigned bgra( uint8_t b, uint8_t g, uint8_t r, uint8_t a );
|
||||
|
@ -3193,7 +3214,6 @@ WINDOW_VSYNC_DISABLED =8192,
|
|||
uint64_t window_frame();
|
||||
int window_width();
|
||||
int window_height();
|
||||
vec2 window_dpi();
|
||||
double window_time();
|
||||
double window_delta();
|
||||
void window_focus();
|
||||
|
@ -3219,6 +3239,7 @@ WINDOW_VSYNC_DISABLED =8192,
|
|||
void window_fps_unlock();
|
||||
void window_screenshot(const char* outfile_png);
|
||||
int window_record(const char *outfile_mp4);
|
||||
vec2 window_dpi();
|
||||
enum CURSOR_SHAPES {
|
||||
CURSOR_NONE,
|
||||
CURSOR_HW_ARROW,
|
||||
|
|
251484
engine/joint/v4k.h
251484
engine/joint/v4k.h
File diff suppressed because it is too large
Load Diff
|
@ -139,6 +139,8 @@ for( int lock_ = (thread_mutex_lock( mutexptr ), 1); lock_; lock_ = (thread_mute
|
|||
|
||||
{{FILE:v4k_pack.c}}
|
||||
|
||||
{{FILE:v4k_reflect.c}}
|
||||
|
||||
{{FILE:v4k_render.c}}
|
||||
|
||||
{{FILE:v4k_renderdd.c}}
|
||||
|
|
|
@ -140,6 +140,8 @@ extern "C" {
|
|||
|
||||
{{FILE:v4k_profile.h}}
|
||||
|
||||
{{FILE:v4k_reflect.h}}
|
||||
|
||||
{{FILE:v4k_render.h}}
|
||||
|
||||
{{FILE:v4k_renderdd.h}}
|
||||
|
|
|
@ -100,6 +100,7 @@ errno_t fopen_s(
|
|||
);
|
||||
#endif
|
||||
|
||||
|
||||
//---
|
||||
{{FILE:3rd_glfw3.h}}
|
||||
#undef timeGetTime
|
||||
|
@ -112,8 +113,6 @@ errno_t fopen_s(
|
|||
{{FILE:3rd_stb_vorbis.h}}
|
||||
#undef error
|
||||
#undef DEBUG
|
||||
// #define MA_DEBUG_OUTPUT
|
||||
// #define MA_USE_AUDIO_WORKLETS
|
||||
{{FILE:3rd_sts_mixer.h}}
|
||||
{{FILE:3rd_miniaudio.h}}
|
||||
//---
|
||||
|
@ -138,7 +137,7 @@ errno_t fopen_s(
|
|||
{{FILE:3rd_nuklear_glfw_gl3.h}}
|
||||
{{FILE:3rd_nuklear_filebrowser.h}}
|
||||
//---
|
||||
#ifdef WITH_ASSIMP
|
||||
#ifdef ENABLE_ASSIMP
|
||||
//{{FILE/*:*/3rd_assimp.h}}
|
||||
//#include "3rd_assimp/cimport.h"
|
||||
//#include "3rd_assimp/scene.h"
|
||||
|
@ -169,17 +168,17 @@ errno_t fopen_s(
|
|||
{{FILE:3rd_xml.h}}
|
||||
#undef g
|
||||
{{FILE:3rd_polychop.h}}
|
||||
#define SQLITE_OMIT_LOAD_EXTENSION
|
||||
#define SQLITE_CORE 1
|
||||
#define SQLITE_DEBUG 1
|
||||
#define Token SQToken
|
||||
#define Table SQTable
|
||||
#define rehash sqlite3__rehash
|
||||
#undef NB
|
||||
{{FILE:3rd_sqlite3.c}}
|
||||
#undef Token
|
||||
#undef Table
|
||||
#undef rehash
|
||||
#undef NB
|
||||
#undef threadid
|
||||
// #define SQLITE_OMIT_LOAD_EXTENSION
|
||||
// #define SQLITE_CORE 1
|
||||
// #define SQLITE_DEBUG 1
|
||||
// #define Token SQToken
|
||||
// #define Table SQTable
|
||||
// #define rehash sqlite3__rehash
|
||||
// #undef NB
|
||||
//{{FILE/*:*/3rd_sqlite3.c}}
|
||||
// #undef Token
|
||||
// #undef Table
|
||||
// #undef rehash
|
||||
// #undef NB
|
||||
// #undef threadid
|
||||
#endif // V4K_3RD
|
||||
|
|
|
@ -1,3 +1,174 @@
|
|||
// Behavior trees: decision planning and decision making.
|
||||
// Supersedes finite state-machines (FSM) and hierarchical finite state-machines (HFSM).
|
||||
// - rlyeh, public domain.
|
||||
//
|
||||
// [ref] https://outforafight.wordpress.com/2014/07/15/behaviour-behavior-trees-for-ai-dudes-part-1/
|
||||
// [ref] https://www.gameaipro.com/GameAIPro/GameAIPro_Chapter06_The_Behavior_Tree_Starter_Kit.pdf
|
||||
// [ref] https://gitlab.com/NotYetGames/DlgSystem/-/wikis/Dialogue-Manual
|
||||
// [ref] https://towardsdatascience.com/designing-ai-agents-behaviors-with-behavior-trees-b28aa1c3cf8a
|
||||
// [ref] https://docs.nvidia.com/isaac/packages/behavior_tree/doc/behavior_trees.html
|
||||
// [ref] https://docs.unrealengine.com/4.26/en-US/InteractiveExperiences/ArtificialIntelligence/BehaviorTrees/
|
||||
// [ref] gdc ChampandardDaweHernandezCerpa_BehaviorTrees.pdf @todo debugging
|
||||
// [ref] https://docs.cocos.com/cocos2d-x/manual/en/actions/
|
||||
|
||||
// The nodes in a behavior tree can be broadly categorized into three main types: control nodes, task nodes, and decorator nodes. Here is a brief description of each category:
|
||||
|
||||
// Control Nodes: Control nodes are used to control the flow of the behavior tree. They determine the order in which child nodes are executed and how the results of those nodes are combined. They are usually parent nodes.
|
||||
// Action Nodes: Action nodes are used to perform specific actions or tasks within the behavior tree. They can include actions such as moving, attacking, or interacting with objects in the game world. They are usually leaf nodes.
|
||||
// Decorator Nodes: Decorator nodes are used to modify the behavior of child nodes in some way. They can be used to repeat child nodes, invert the result of a child node, or add a cooldown period between executions. They are usually located between control and action nodes.
|
||||
|
||||
// --- VARIABLES
|
||||
|
||||
// Key Prefixes:
|
||||
// {visiblity:(l)ocal,s(q)uad,(r)ace,(f)action,(g)lobal}
|
||||
// [persistence:(t)emp,(u)serdata,(s)avegame,(c)loud] + '_' + name.
|
||||
// [persistence:(tmp),(usr)data,(sav)egame,(net)cloud] + '_' + name.
|
||||
|
||||
// Ie, l_health = 123.4, gsav_player = "john"
|
||||
|
||||
// --- ACTION NODES
|
||||
|
||||
// [ ] * Actions/Behaviors have a common structure:
|
||||
// [ ] - Entry point (function name) for a C call or Lua script.
|
||||
// [ ] * Status:
|
||||
// [ ] - Uninitialized (never run)
|
||||
// [ ] - Running (in progress)
|
||||
// [ ] - Suspended (on hold till resumed)
|
||||
// [ ] - Success (finished and succeeded)
|
||||
// [ ] - Failure (finished and failed)
|
||||
// [ ] * Optional callbacks:
|
||||
// [ ] - on_enter
|
||||
// [ ] - on_leave
|
||||
// [ ] - on_success
|
||||
// [ ] - on_failure
|
||||
// [ ] - on_suspend
|
||||
// [ ] - on_resume
|
||||
|
||||
// [x] Action Node: This node performs a single action, such as moving to a specific location or attacking a target.
|
||||
// [ ] Blackboard Node: Node that reads and writes data to a shared memory space known as a blackboard. The blackboard can be used to store information that is relevant to multiple nodes in the behavior tree.
|
||||
// [ ] SetKey(keyVar,object)
|
||||
// [ ] HasKey(keyVar)
|
||||
// [ ] CompareKeys(keyVar1, operator < <= > >= == !=, keyVar2)
|
||||
// [ ] SetTags(names=blank,cooldownTime=inf,bIsCooldownAdditive=false)
|
||||
// [ ] HasTags(names=blank,bAllRequired=true)
|
||||
// [ ] PushToStack(keyVar,itemObj): creates a new stack if one doesn’t exist, and stores it in the passed variable name, and then pushes ‘item’ object onto it.
|
||||
// [ ] PopFromStack(keyVar,itemVar): pop pops an item off the stack, and stores it in the itemVar variable, failing if the stack is already empty.
|
||||
// [ ] IsEmptyStack(keyVar): checks if the stack passed is empty and returns success if it is, and failure if its not.
|
||||
// [ ] Communication Node: This is a type of action node that allows an AI agent to communicate with other agents or entities in the game world. The node takes an input specifying the message to be communicated and the recipient(s) of the message (wildmask,l/p/f/g prefixes). The node then sends the message to the designated recipient(s) and returns success when the communication is completed. This node can be useful for implementing behaviors that require the AI agent to coordinate with other agents or to convey information to the player. It could use a radius argument to specify the maximum allowed distance for the recipients.
|
||||
// [ ] Condition Node: A leaf node that checks a specific condition, such as the distance to an object, the presence of an enemy, or the status of a health bar.
|
||||
// [ ] Distance Condition Node: This is a type of condition node that evaluates whether an AI agent is within a specified distance of a target object or location. The node takes two inputs: the current position of the AI agent and the position of the target object or location. If the distance between the two is within a specified range, the node returns success. If the distance is outside of the specified range, the node returns failure. This node can be useful for implementing behaviors that require the AI agent to maintain a certain distance from a target, such as following or avoiding an object. Could use a flag to disambiguate between linear distance and path distance.
|
||||
// [ ] Query Node: This node checks a condition and returns success or failure based on the result. For example, a query node could check whether an enemy is within range or whether a door is locked.
|
||||
// [ ] Query Node: A type of decorator node that retrieves information from a database or external system, such as a web service or file system. The Query node can be used to retrieve data that is not available within the game engine, such as weather or traffic conditions.
|
||||
// [ ] A condition is made of:
|
||||
// [ ] - Optional [!] negate
|
||||
// [ ] - Mandatory Value1(Int/Flt/Bool/VarName/FuncName)
|
||||
// [ ] - Optional operator [< <= > >= == !=] and Value2(Int/Flt/Bool/VarName/FuncName)
|
||||
// [ ] AllConditions(...) : SUCCESS if ( empty array || all conditions met)
|
||||
// [ ] AnyConditions(...) : SUCCESS if (!empty array && one conditions met)
|
||||
|
||||
// --- DECORATOR NODES
|
||||
|
||||
// [ ] Cooldown Node: Decorator node that adds a cooldown period between the execution of a child node, preventing it from being executed again until the cooldown period has elapsed.
|
||||
// [x] Counter Node: Decorator node that limits the number of times that a child node can execute. For example, if the child node has executed a certain number of times, the Counter node will return a failure.
|
||||
// [x] Once Node: Decorator node that triggers a specified action when a condition is met for the first time. This can be useful for triggering a one-time event, such as when an AI agent discovers a hidden item or reaches a new area of the game world.
|
||||
// [x] Inverter Node: Decorator node that inverts the result of a child node, returning success if the child node fails and failure if the child node succeeds. This can be useful for negating the outcome of a particular behavior, such as avoiding a certain area of the game world.
|
||||
// [x] Repeater Node: Decorator node that repeats the execution of a child node a specified number of times or indefinitely.
|
||||
// [x] Repeat(times=inf): Runs child node given times. These are often used at the very base of the tree, to make the tree to run continuously.
|
||||
// [ ] RepeatIf(strong/weak condition): Runs child node as long as the conditions are met.
|
||||
// [ ] RepeatIfOk(times=inf): Runs child node if it succeedes, max given times.
|
||||
// [ ] RepeatIfFail(times=inf): Runs child node if it fails, max given times.
|
||||
// [ ] Branch Node: 2 children [0] for true, [1] for false
|
||||
// [ ] Resource Node: Decorator node that manages a shared resource, such as a limited supply of ammunition or a limited amount of processing power. The Resource node The node can be used to decide when to use or conserve the resource. For example, if the AI agent is low on ammunition, the node may decide to switch to a melee weapon or to retreat to a safer location. This node can be useful for implementing behaviors that require the AI agent to make strategic decisions about resource use.
|
||||
// [x] Result Node: Decorator node that tracks the result of a child action node and returns either success or failure depending on the outcome. This can be useful for determining whether an action was successful or not, and then adjusting the AI agent's behavior accordingly.
|
||||
// [x] Succeeder Node: Decorator node that always returns success, regardless of the result of its child node. This can be useful for ensuring that certain behaviors are always executed, regardless of the overall success or failure of the behavior tree.
|
||||
// [x] Success(): FAILURE becomes SUCCESS (TRUE).
|
||||
// [x] Failure(): SUCCESS becomes FAILURE (FALSE).
|
||||
// [ ] Throttle Node: Decorator node that limits the rate at which a child node can execute. For example, a Throttle node might ensure that an AI agent can only fire its weapon, or using a special ability, only a certain number of times per second.
|
||||
// [x] Delay Node: Decorator node that adds a delay to the execution of a child node. The delay might be configured to sleep before the execution, after the execution, or both.
|
||||
// [x] Defer Delay(duration_ss=1): Runs the child node, then sleeps for given seconds.
|
||||
// [ ] Ease(time,name): Clamps child time to [0,1] range, and applies easing function on it.
|
||||
// [ ] Dilate(Mul=1,Add=0): Dilates child time
|
||||
|
||||
// --- CONTROL NODES
|
||||
|
||||
// [x] Root Node: The topmost node in a behavior tree that represents the start of the decision-making process. Returns success if any of its child nodes suceedes.
|
||||
// [x] Root-Failure Node: Control node that only returns failure if all of its child nodes fail. This can be useful for ensuring that a behavior tree is not prematurely terminated if one or more child nodes fail.
|
||||
|
||||
// [ ] Event(name): When name event is raised, it suspends current tree and calls child. "Incoming projectile" -> Evade. Stimulus types: may be disabled by event, or autodisabled.
|
||||
// [ ] Raise(name): Triggers event name.
|
||||
|
||||
// [ ] Checkpoint Node: Control node that saves a state in memory and then continues the tree execution from that point the next time the tree is executed. It can be useful in situations where the behavior tree needs to be interrupted and resumed later.
|
||||
// [ ] Decision Making Node: Control node that implements a decision-making process for the AI agent. The node takes input specifying the available options for the AI agent and the criteria for evaluating each option. The node then evaluates each option based on the specified criteria and selects the best option. This node can be useful for implementing behaviors that require the AI agent to make strategic decisions, such as choosing a target or selecting a path through the game world.
|
||||
// [ ] Could be extended with GOAP if dynamically inserting the scores on each update then calling a Probability Selector Node (0.2,0.3,0.5)
|
||||
// [ ] https://cdn.cloudflare.steamstatic.com/apps/valve/2012/GDC2012_Ruskin_Elan_DynamicDialog.pdf
|
||||
// [ ] Evaluate Node / Recheck():
|
||||
// [ ] Actively rechecks all existing sub-conditions on a regular basis after having made decisions about them.
|
||||
// [ ] Use this feature to dynamically check for risks or opportunities in selected parts of the tree.
|
||||
// [ ] For example, interrupting a patrol with a search behavior if a disturbance is reported.
|
||||
// [ ] Interrupt Node: Control node that interrupts the execution of a lower-priority node when a higher-priority node needs to be executed. It can be useful for handling urgent tasks or emergency situations.
|
||||
// [ ] Monitor Node: Control node that continuously monitors a condition and triggers a specified action when the condition is met. For example, a Monitor node might monitor the distance between an AI agent and a target and trigger a "retreat" behavior when the distance falls below a certain threshold.
|
||||
// [ ] Observer Node: Control node that monitors the state of the game world and triggers a specified action when certain conditions are met. For example, an Observer node might trigger a "hide" behavior when an enemy is spotted.
|
||||
// [ ] Parallel Node: Control node that executes multiple child nodes simultaneously. The Parallel node continues to execute even if some of its child nodes fail.
|
||||
// [ ] Parallel All Node(required_successes=100%): Control node that executes multiple child nodes simultaneously. Returns false when first child fails, and aborts any other running tasks. Returns true if all its children succeed.
|
||||
// [ ] Parallel One Node(required_successes=1): Control node that executes multiple child nodes simultaneously. Returns true when first child suceedes, and aborts any other running tasks. Returns false if all its children fail.
|
||||
// [ ] Subsumption Node: Control node that allows multiple behaviors to be executed simultaneously, with higher-priority behaviors taking precedence over lower-priority ones. This can be useful for implementing complex, multi-level behaviors in autonomous systems.
|
||||
// [ ] Semaphore Node: Control node that blocks the execution of its child nodes until a certain condition is met. For example, a Semaphore node might block the execution of a behavior until a certain object is in range or a certain event occurs.
|
||||
// [ ] Semaphore Wait Node: Control node that blocks the execution of its child nodes until a resource becomes available. This can be useful for controlling access to shared resources such as a pathfinding system or a communication channel.
|
||||
// [ ] WaitTags(tags=blank,timeout_ss=inf): Stops execution of child node until the cooldown tag(s) do expire. May return earlier if timed out.
|
||||
// [ ] WaitEvent(name=blank,timeout_ss=inf): Stops execution of child node until event is raised. May return earlier if timed out.
|
||||
// [x] Sequence Node(reversed,iterator(From,To)): Control node that executes a series of child nodes in order. If any of the child nodes fail, the Sequence node stops executing and returns a failure.
|
||||
// [ ] Dynamic Sequence Node: Control node that dynamically changes the order in which its child nodes are executed based on certain conditions. For example, a Dynamic Sequence node might give higher priority to a child node that has recently failed or that is more likely to succeed.
|
||||
// [ ] Reverse(): iterates children in reversed order (Iterator:(-1,0) equivalent)
|
||||
// [ ] Iterator(from,to): allows negative indexing. increments if (abs from < abs to), decrements otherwise
|
||||
// [x] Selector Node: Control node that selects a child node to execute based on a predefined priority or set of conditions. The Selector node stops executing child nodes as soon as one of them succeeds.
|
||||
// [ ] Priority Selector Node: Control node that executes its child nodes in order of priority. If the first child node fails, it moves on to the next child node in the list until a successful node is found. This can be useful for implementing behaviors that require a specific order of execution, such as a patrol route or a search pattern.
|
||||
// [ ] Probability Selector Node: Control node that selects a child node to execute based on a probability distribution. For example, if there are three child nodes with probabilities of 0.2, 0.3, and 0.5, the Probability Selector node will execute the third child node 50% of the time.
|
||||
// [ ] Dynamic Selector Node: Control node that dynamically changes the order in which its child nodes are executed based on certain conditions. For example, a Dynamic Selector node might give higher priority to a child node that has recently succeeded or that is more likely to succeed.
|
||||
// [ ] Dynamic Selector Node: Control node that dynamically changes the order in which child nodes are executed based on certain conditions. For example, it may prioritize certain nodes when health is low, and other nodes when resources are scarce.
|
||||
// [ ] Dynamic Priority Node: Control node that dynamically changes the priority of its child nodes based on certain conditions. For example, if a child node is more likely to result in success, the Dynamic Priority node will give it a higher priority.
|
||||
// [ ] Weighted / Cover Selection Node: This is a type of selector node that evaluates the available cover options in the game world and selects the best cover position for the AI agent. The node takes input specifying the current position of the AI agent and the location of potential cover positions. The node then evaluates the cover positions based on criteria such as distance to enemy positions, line of sight, and available cover points, and selects the best option. This node can be useful for implementing behaviors that require the AI agent to take cover and avoid enemy fire.
|
||||
// [ ] Random Selector Node: Control node that selects a child node to execute at random. This can be useful for introducing variety and unpredictability into the behavior of an AI agent.
|
||||
// [ ] Random Weight / Stochastic Node(0.2,0.3,0.5): Control node that introduces randomness into the decision-making process of an AI agent. For example, a Stochastic node might randomly select a child node to execute, with the probability of each child node being proportional to its likelihood of success.
|
||||
// [ ] Break(bool): breaks parent sequence or selector. may return SUCCESS or FAILURE.
|
||||
// [ ] Hybrid Node: Control node that combines the functionality of multiple control nodes, such as a sequence node and a selector node. It can be useful for creating complex behavior patterns that require multiple control structures. The Hybrid Node has two modes of operation: strict mode and flexible mode. In strict mode, the child nodes are executed in a fixed order, similar to a Sequence Node. If any child node fails, the entire Hybrid Node fails. In flexible mode, the child nodes can be executed in any order, similar to a Selector Node. If any child node succeeds, the entire Hybrid Node succeeds. The Hybrid Node is often used when you need to create complex behavior patterns that require multiple control structures. For example, you might use a Hybrid Node to implement a search behavior where the AI agent first moves in a fixed direction for a certain distance (strict mode), but then switches to a more exploratory mode where it randomly explores nearby areas (flexible mode).
|
||||
// [ ] Subtree Node: Control node that calls another behavior tree as a subroutine. This is useful for breaking down complex behaviors into smaller, more manageable parts.
|
||||
// [ ] Call(name): Calls to the child node with the matching name within behavior tree. Returns FAILURE if name is invalid or if invoked behavior returns FAILURE.
|
||||
// [ ] Return(boolean): Exits current Call.
|
||||
// [ ] AttachTree(Name,bDetachAfterUse=true): Attaches subtree to main tree. When a NPC founds the actor, it attaches the behavior to the tree. Ie, Level specific content: Patrols, Initial setups, Story driven events, etc. Ie, DLCs: Behaviors are added to actors in the DLC level (enticers).
|
||||
// [ ] DetachTree(Name)
|
||||
// [ ] Switch Node: Control node that evaluates a condition and then selects one of several possible child nodes to execute based on the result of the condition.
|
||||
// [ ] Switch(name): Jumps to the child node with the matching name within behavior tree. If name is invalid will defer to Fallback child node.
|
||||
// [ ] Timeout Node: Control node that aborts the execution of its child node after a certain amount of time has elapsed. This can be useful for preventing an AI agent from getting stuck in a loop or waiting indefinitely for a particular event to occur.
|
||||
// [ ] TimeLimit(timeout_ss=inf): Give the child any amount of time to finish before it gets canceled. The timer is reset every time the node gains focus.
|
||||
// [ ] Timer Node: Control node which invokes its child node every XX seconds. The timer can repeat the action any given number of times, or indefinitely.
|
||||
|
||||
// ## Proposal -----------------------------------------------------------------------------
|
||||
|
||||
// BehaviorTrees as case-insensitive INI files. Then,
|
||||
// - INI -> C INTERPRETER, OR
|
||||
// - INI -> LUA TRANSPILER -> LUA BYTECODE -> LUA VM.
|
||||
|
||||
// ```ini
|
||||
// [bt]
|
||||
// recheck
|
||||
// sequence ;; Approach the player if seen!
|
||||
// conditions=IsPlayerVisible,
|
||||
// action=MoveTowardsPlayer
|
||||
// sequence ;; Attack the player if seen!
|
||||
// conditions=IsPlayerInRange,
|
||||
// repeat=3
|
||||
// action=FireAtPlayer
|
||||
// sequence ;; Search near last known position
|
||||
// conditions=HaveWeGotASuspectedLocation,
|
||||
// action=MoveToPlayersLastKnownPosition
|
||||
// action=LookAround
|
||||
// sequence ;; Randomly scanning nearby
|
||||
// action=MoveToRandomPosition
|
||||
// action=LookAround
|
||||
// event=IncomingProjectile
|
||||
// action=Evade
|
||||
// ```
|
||||
|
||||
map(char*, bt_func) binds;
|
||||
|
||||
void bt_addfun(const char *name, int(*func)()){
|
||||
|
|
|
@ -1,173 +1,6 @@
|
|||
// Behavior trees: decision planning and decision making.
|
||||
// Supersedes finite state-machines (FSM) and hierarchical finite state-machines (HFSM).
|
||||
// - rlyeh, public domain.
|
||||
//
|
||||
// [ref] https://outforafight.wordpress.com/2014/07/15/behaviour-behavior-trees-for-ai-dudes-part-1/
|
||||
// [ref] https://www.gameaipro.com/GameAIPro/GameAIPro_Chapter06_The_Behavior_Tree_Starter_Kit.pdf
|
||||
// [ref] https://gitlab.com/NotYetGames/DlgSystem/-/wikis/Dialogue-Manual
|
||||
// [ref] https://towardsdatascience.com/designing-ai-agents-behaviors-with-behavior-trees-b28aa1c3cf8a
|
||||
// [ref] https://docs.nvidia.com/isaac/packages/behavior_tree/doc/behavior_trees.html
|
||||
// [ref] https://docs.unrealengine.com/4.26/en-US/InteractiveExperiences/ArtificialIntelligence/BehaviorTrees/
|
||||
// [ref] gdc ChampandardDaweHernandezCerpa_BehaviorTrees.pdf @todo debugging
|
||||
// [ref] https://docs.cocos.com/cocos2d-x/manual/en/actions/
|
||||
|
||||
// The nodes in a behavior tree can be broadly categorized into three main types: control nodes, task nodes, and decorator nodes. Here is a brief description of each category:
|
||||
|
||||
// Control Nodes: Control nodes are used to control the flow of the behavior tree. They determine the order in which child nodes are executed and how the results of those nodes are combined. They are usually parent nodes.
|
||||
// Action Nodes: Action nodes are used to perform specific actions or tasks within the behavior tree. They can include actions such as moving, attacking, or interacting with objects in the game world. They are usually leaf nodes.
|
||||
// Decorator Nodes: Decorator nodes are used to modify the behavior of child nodes in some way. They can be used to repeat child nodes, invert the result of a child node, or add a cooldown period between executions. They are usually located between control and action nodes.
|
||||
|
||||
// --- VARIABLES
|
||||
|
||||
// Key Prefixes:
|
||||
// {visiblity:(l)ocal,s(q)uad,(r)ace,(f)action,(g)lobal}
|
||||
// [persistence:(t)emp,(u)serdata,(s)avegame,(c)loud] + '_' + name.
|
||||
// [persistence:(tmp),(usr)data,(sav)egame,(net)cloud] + '_' + name.
|
||||
|
||||
// Ie, l_health = 123.4, gsav_player = "john"
|
||||
|
||||
// --- ACTION NODES
|
||||
|
||||
// [ ] * Actions/Behaviors have a common structure:
|
||||
// [ ] - Entry point (function name) for a C call or Lua script.
|
||||
// [ ] * Status:
|
||||
// [ ] - Uninitialized (never run)
|
||||
// [ ] - Running (in progress)
|
||||
// [ ] - Suspended (on hold till resumed)
|
||||
// [ ] - Success (finished and succeeded)
|
||||
// [ ] - Failure (finished and failed)
|
||||
// [ ] * Optional callbacks:
|
||||
// [ ] - on_enter
|
||||
// [ ] - on_leave
|
||||
// [ ] - on_success
|
||||
// [ ] - on_failure
|
||||
// [ ] - on_suspend
|
||||
// [ ] - on_resume
|
||||
|
||||
// [x] Action Node: This node performs a single action, such as moving to a specific location or attacking a target.
|
||||
// [ ] Blackboard Node: Node that reads and writes data to a shared memory space known as a blackboard. The blackboard can be used to store information that is relevant to multiple nodes in the behavior tree.
|
||||
// [ ] SetKey(keyVar,object)
|
||||
// [ ] HasKey(keyVar)
|
||||
// [ ] CompareKeys(keyVar1, operator < <= > >= == !=, keyVar2)
|
||||
// [ ] SetTags(names=blank,cooldownTime=inf,bIsCooldownAdditive=false)
|
||||
// [ ] HasTags(names=blank,bAllRequired=true)
|
||||
// [ ] PushToStack(keyVar,itemObj): creates a new stack if one doesn’t exist, and stores it in the passed variable name, and then pushes ‘item’ object onto it.
|
||||
// [ ] PopFromStack(keyVar,itemVar): pop pops an item off the stack, and stores it in the itemVar variable, failing if the stack is already empty.
|
||||
// [ ] IsEmptyStack(keyVar): checks if the stack passed is empty and returns success if it is, and failure if its not.
|
||||
// [ ] Communication Node: This is a type of action node that allows an AI agent to communicate with other agents or entities in the game world. The node takes an input specifying the message to be communicated and the recipient(s) of the message (wildmask,l/p/f/g prefixes). The node then sends the message to the designated recipient(s) and returns success when the communication is completed. This node can be useful for implementing behaviors that require the AI agent to coordinate with other agents or to convey information to the player. It could use a radius argument to specify the maximum allowed distance for the recipients.
|
||||
// [ ] Condition Node: A leaf node that checks a specific condition, such as the distance to an object, the presence of an enemy, or the status of a health bar.
|
||||
// [ ] Distance Condition Node: This is a type of condition node that evaluates whether an AI agent is within a specified distance of a target object or location. The node takes two inputs: the current position of the AI agent and the position of the target object or location. If the distance between the two is within a specified range, the node returns success. If the distance is outside of the specified range, the node returns failure. This node can be useful for implementing behaviors that require the AI agent to maintain a certain distance from a target, such as following or avoiding an object. Could use a flag to disambiguate between linear distance and path distance.
|
||||
// [ ] Query Node: This node checks a condition and returns success or failure based on the result. For example, a query node could check whether an enemy is within range or whether a door is locked.
|
||||
// [ ] Query Node: A type of decorator node that retrieves information from a database or external system, such as a web service or file system. The Query node can be used to retrieve data that is not available within the game engine, such as weather or traffic conditions.
|
||||
// [ ] A condition is made of:
|
||||
// [ ] - Optional [!] negate
|
||||
// [ ] - Mandatory Value1(Int/Flt/Bool/VarName/FuncName)
|
||||
// [ ] - Optional operator [< <= > >= == !=] and Value2(Int/Flt/Bool/VarName/FuncName)
|
||||
// [ ] AllConditions(...) : SUCCESS if ( empty array || all conditions met)
|
||||
// [ ] AnyConditions(...) : SUCCESS if (!empty array && one conditions met)
|
||||
|
||||
// --- DECORATOR NODES
|
||||
|
||||
// [ ] Cooldown Node: Decorator node that adds a cooldown period between the execution of a child node, preventing it from being executed again until the cooldown period has elapsed.
|
||||
// [x] Counter Node: Decorator node that limits the number of times that a child node can execute. For example, if the child node has executed a certain number of times, the Counter node will return a failure.
|
||||
// [x] Once Node: Decorator node that triggers a specified action when a condition is met for the first time. This can be useful for triggering a one-time event, such as when an AI agent discovers a hidden item or reaches a new area of the game world.
|
||||
// [x] Inverter Node: Decorator node that inverts the result of a child node, returning success if the child node fails and failure if the child node succeeds. This can be useful for negating the outcome of a particular behavior, such as avoiding a certain area of the game world.
|
||||
// [x] Repeater Node: Decorator node that repeats the execution of a child node a specified number of times or indefinitely.
|
||||
// [x] Repeat(times=inf): Runs child node given times. These are often used at the very base of the tree, to make the tree to run continuously.
|
||||
// [ ] RepeatIf(strong/weak condition): Runs child node as long as the conditions are met.
|
||||
// [ ] RepeatIfOk(times=inf): Runs child node if it succeedes, max given times.
|
||||
// [ ] RepeatIfFail(times=inf): Runs child node if it fails, max given times.
|
||||
// [ ] Branch Node: 2 children [0] for true, [1] for false
|
||||
// [ ] Resource Node: Decorator node that manages a shared resource, such as a limited supply of ammunition or a limited amount of processing power. The Resource node The node can be used to decide when to use or conserve the resource. For example, if the AI agent is low on ammunition, the node may decide to switch to a melee weapon or to retreat to a safer location. This node can be useful for implementing behaviors that require the AI agent to make strategic decisions about resource use.
|
||||
// [x] Result Node: Decorator node that tracks the result of a child action node and returns either success or failure depending on the outcome. This can be useful for determining whether an action was successful or not, and then adjusting the AI agent's behavior accordingly.
|
||||
// [x] Succeeder Node: Decorator node that always returns success, regardless of the result of its child node. This can be useful for ensuring that certain behaviors are always executed, regardless of the overall success or failure of the behavior tree.
|
||||
// [x] Success(): FAILURE becomes SUCCESS (TRUE).
|
||||
// [x] Failure(): SUCCESS becomes FAILURE (FALSE).
|
||||
// [ ] Throttle Node: Decorator node that limits the rate at which a child node can execute. For example, a Throttle node might ensure that an AI agent can only fire its weapon, or using a special ability, only a certain number of times per second.
|
||||
// [x] Delay Node: Decorator node that adds a delay to the execution of a child node. The delay might be configured to sleep before the execution, after the execution, or both.
|
||||
// [x] Defer Delay(duration_ss=1): Runs the child node, then sleeps for given seconds.
|
||||
// [ ] Ease(time,name): Clamps child time to [0,1] range, and applies easing function on it.
|
||||
// [ ] Dilate(Mul=1,Add=0): Dilates child time
|
||||
|
||||
// --- CONTROL NODES
|
||||
|
||||
// [x] Root Node: The topmost node in a behavior tree that represents the start of the decision-making process. Returns success if any of its child nodes suceedes.
|
||||
// [x] Root-Failure Node: Control node that only returns failure if all of its child nodes fail. This can be useful for ensuring that a behavior tree is not prematurely terminated if one or more child nodes fail.
|
||||
|
||||
// [ ] Event(name): When name event is raised, it suspends current tree and calls child. "Incoming projectile" -> Evade. Stimulus types: may be disabled by event, or autodisabled.
|
||||
// [ ] Raise(name): Triggers event name.
|
||||
|
||||
// [ ] Checkpoint Node: Control node that saves a state in memory and then continues the tree execution from that point the next time the tree is executed. It can be useful in situations where the behavior tree needs to be interrupted and resumed later.
|
||||
// [ ] Decision Making Node: Control node that implements a decision-making process for the AI agent. The node takes input specifying the available options for the AI agent and the criteria for evaluating each option. The node then evaluates each option based on the specified criteria and selects the best option. This node can be useful for implementing behaviors that require the AI agent to make strategic decisions, such as choosing a target or selecting a path through the game world.
|
||||
// [ ] Could be extended with GOAP if dynamically inserting the scores on each update then calling a Probability Selector Node (0.2,0.3,0.5)
|
||||
// [ ] https://cdn.cloudflare.steamstatic.com/apps/valve/2012/GDC2012_Ruskin_Elan_DynamicDialog.pdf
|
||||
// [ ] Evaluate Node / Recheck():
|
||||
// [ ] Actively rechecks all existing sub-conditions on a regular basis after having made decisions about them.
|
||||
// [ ] Use this feature to dynamically check for risks or opportunities in selected parts of the tree.
|
||||
// [ ] For example, interrupting a patrol with a search behavior if a disturbance is reported.
|
||||
// [ ] Interrupt Node: Control node that interrupts the execution of a lower-priority node when a higher-priority node needs to be executed. It can be useful for handling urgent tasks or emergency situations.
|
||||
// [ ] Monitor Node: Control node that continuously monitors a condition and triggers a specified action when the condition is met. For example, a Monitor node might monitor the distance between an AI agent and a target and trigger a "retreat" behavior when the distance falls below a certain threshold.
|
||||
// [ ] Observer Node: Control node that monitors the state of the game world and triggers a specified action when certain conditions are met. For example, an Observer node might trigger a "hide" behavior when an enemy is spotted.
|
||||
// [ ] Parallel Node: Control node that executes multiple child nodes simultaneously. The Parallel node continues to execute even if some of its child nodes fail.
|
||||
// [ ] Parallel All Node(required_successes=100%): Control node that executes multiple child nodes simultaneously. Returns false when first child fails, and aborts any other running tasks. Returns true if all its children succeed.
|
||||
// [ ] Parallel One Node(required_successes=1): Control node that executes multiple child nodes simultaneously. Returns true when first child suceedes, and aborts any other running tasks. Returns false if all its children fail.
|
||||
// [ ] Subsumption Node: Control node that allows multiple behaviors to be executed simultaneously, with higher-priority behaviors taking precedence over lower-priority ones. This can be useful for implementing complex, multi-level behaviors in autonomous systems.
|
||||
// [ ] Semaphore Node: Control node that blocks the execution of its child nodes until a certain condition is met. For example, a Semaphore node might block the execution of a behavior until a certain object is in range or a certain event occurs.
|
||||
// [ ] Semaphore Wait Node: Control node that blocks the execution of its child nodes until a resource becomes available. This can be useful for controlling access to shared resources such as a pathfinding system or a communication channel.
|
||||
// [ ] WaitTags(tags=blank,timeout_ss=inf): Stops execution of child node until the cooldown tag(s) do expire. May return earlier if timed out.
|
||||
// [ ] WaitEvent(name=blank,timeout_ss=inf): Stops execution of child node until event is raised. May return earlier if timed out.
|
||||
// [x] Sequence Node(reversed,iterator(From,To)): Control node that executes a series of child nodes in order. If any of the child nodes fail, the Sequence node stops executing and returns a failure.
|
||||
// [ ] Dynamic Sequence Node: Control node that dynamically changes the order in which its child nodes are executed based on certain conditions. For example, a Dynamic Sequence node might give higher priority to a child node that has recently failed or that is more likely to succeed.
|
||||
// [ ] Reverse(): iterates children in reversed order (Iterator:(-1,0) equivalent)
|
||||
// [ ] Iterator(from,to): allows negative indexing. increments if (abs from < abs to), decrements otherwise
|
||||
// [x] Selector Node: Control node that selects a child node to execute based on a predefined priority or set of conditions. The Selector node stops executing child nodes as soon as one of them succeeds.
|
||||
// [ ] Priority Selector Node: Control node that executes its child nodes in order of priority. If the first child node fails, it moves on to the next child node in the list until a successful node is found. This can be useful for implementing behaviors that require a specific order of execution, such as a patrol route or a search pattern.
|
||||
// [ ] Probability Selector Node: Control node that selects a child node to execute based on a probability distribution. For example, if there are three child nodes with probabilities of 0.2, 0.3, and 0.5, the Probability Selector node will execute the third child node 50% of the time.
|
||||
// [ ] Dynamic Selector Node: Control node that dynamically changes the order in which its child nodes are executed based on certain conditions. For example, a Dynamic Selector node might give higher priority to a child node that has recently succeeded or that is more likely to succeed.
|
||||
// [ ] Dynamic Selector Node: Control node that dynamically changes the order in which child nodes are executed based on certain conditions. For example, it may prioritize certain nodes when health is low, and other nodes when resources are scarce.
|
||||
// [ ] Dynamic Priority Node: Control node that dynamically changes the priority of its child nodes based on certain conditions. For example, if a child node is more likely to result in success, the Dynamic Priority node will give it a higher priority.
|
||||
// [ ] Weighted / Cover Selection Node: This is a type of selector node that evaluates the available cover options in the game world and selects the best cover position for the AI agent. The node takes input specifying the current position of the AI agent and the location of potential cover positions. The node then evaluates the cover positions based on criteria such as distance to enemy positions, line of sight, and available cover points, and selects the best option. This node can be useful for implementing behaviors that require the AI agent to take cover and avoid enemy fire.
|
||||
// [ ] Random Selector Node: Control node that selects a child node to execute at random. This can be useful for introducing variety and unpredictability into the behavior of an AI agent.
|
||||
// [ ] Random Weight / Stochastic Node(0.2,0.3,0.5): Control node that introduces randomness into the decision-making process of an AI agent. For example, a Stochastic node might randomly select a child node to execute, with the probability of each child node being proportional to its likelihood of success.
|
||||
// [ ] Break(bool): breaks parent sequence or selector. may return SUCCESS or FAILURE.
|
||||
// [ ] Hybrid Node: Control node that combines the functionality of multiple control nodes, such as a sequence node and a selector node. It can be useful for creating complex behavior patterns that require multiple control structures. The Hybrid Node has two modes of operation: strict mode and flexible mode. In strict mode, the child nodes are executed in a fixed order, similar to a Sequence Node. If any child node fails, the entire Hybrid Node fails. In flexible mode, the child nodes can be executed in any order, similar to a Selector Node. If any child node succeeds, the entire Hybrid Node succeeds. The Hybrid Node is often used when you need to create complex behavior patterns that require multiple control structures. For example, you might use a Hybrid Node to implement a search behavior where the AI agent first moves in a fixed direction for a certain distance (strict mode), but then switches to a more exploratory mode where it randomly explores nearby areas (flexible mode).
|
||||
// [ ] Subtree Node: Control node that calls another behavior tree as a subroutine. This is useful for breaking down complex behaviors into smaller, more manageable parts.
|
||||
// [ ] Call(name): Calls to the child node with the matching name within behavior tree. Returns FAILURE if name is invalid or if invoked behavior returns FAILURE.
|
||||
// [ ] Return(boolean): Exits current Call.
|
||||
// [ ] AttachTree(Name,bDetachAfterUse=true): Attaches subtree to main tree. When a NPC founds the actor, it attaches the behavior to the tree. Ie, Level specific content: Patrols, Initial setups, Story driven events, etc. Ie, DLCs: Behaviors are added to actors in the DLC level (enticers).
|
||||
// [ ] DetachTree(Name)
|
||||
// [ ] Switch Node: Control node that evaluates a condition and then selects one of several possible child nodes to execute based on the result of the condition.
|
||||
// [ ] Switch(name): Jumps to the child node with the matching name within behavior tree. If name is invalid will defer to Fallback child node.
|
||||
// [ ] Timeout Node: Control node that aborts the execution of its child node after a certain amount of time has elapsed. This can be useful for preventing an AI agent from getting stuck in a loop or waiting indefinitely for a particular event to occur.
|
||||
// [ ] TimeLimit(timeout_ss=inf): Give the child any amount of time to finish before it gets canceled. The timer is reset every time the node gains focus.
|
||||
// [ ] Timer Node: Control node which invokes its child node every XX seconds. The timer can repeat the action any given number of times, or indefinitely.
|
||||
|
||||
// ## Proposal -----------------------------------------------------------------------------
|
||||
|
||||
// BehaviorTrees as case-insensitive INI files. Then,
|
||||
// - INI -> C INTERPRETER, OR
|
||||
// - INI -> LUA TRANSPILER -> LUA BYTECODE -> LUA VM.
|
||||
|
||||
// ```ini
|
||||
// [bt]
|
||||
// recheck
|
||||
// sequence ;; Approach the player if seen!
|
||||
// conditions=IsPlayerVisible,
|
||||
// action=MoveTowardsPlayer
|
||||
// sequence ;; Attack the player if seen!
|
||||
// conditions=IsPlayerInRange,
|
||||
// repeat=3
|
||||
// action=FireAtPlayer
|
||||
// sequence ;; Search near last known position
|
||||
// conditions=HaveWeGotASuspectedLocation,
|
||||
// action=MoveToPlayersLastKnownPosition
|
||||
// action=LookAround
|
||||
// sequence ;; Randomly scanning nearby
|
||||
// action=MoveToRandomPosition
|
||||
// action=LookAround
|
||||
// event=IncomingProjectile
|
||||
// action=Evade
|
||||
// ```
|
||||
|
||||
typedef int (*bt_func)();
|
||||
|
||||
|
|
|
@ -25,6 +25,10 @@
|
|||
#define ENABLE_LINUX_CALLSTACKS 0 ///+
|
||||
#endif
|
||||
|
||||
#ifndef ENABLE_TESTS
|
||||
#define ENABLE_TESTS 0 // ifdef(debug, 1, 0) ///+
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// if/n/def hell
|
||||
|
||||
|
@ -116,7 +120,6 @@
|
|||
|
||||
// -----------------------------------------------------------------------------
|
||||
// new C keywords
|
||||
// @todo: autorun (needed?)
|
||||
|
||||
#define countof(x) (int)(sizeof (x) / sizeof 0[x])
|
||||
|
||||
|
@ -126,13 +129,13 @@
|
|||
#define macro(name) concat(name, __LINE__)
|
||||
#define defer(begin,end) for(int macro(i) = ((begin), 0); !macro(i); macro(i) = ((end), 1))
|
||||
#define scope(end) defer((void)0, end)
|
||||
#define benchmark for(double macro(t) = -time_ss(); macro(t) < 0; printf("%.2fs (" FILELINE ")\n", macro(t)+=time_ss()))
|
||||
#define benchmark for(double macro(i) = 1, macro(t) = -time_ss(); macro(i); macro(t)+=time_ss(), macro(i)=0, printf("%.4fs %2.f%% (" FILELINE ")\n", macro(t), macro(t)*100/0.0166667 ))
|
||||
#define do_once static int macro(once) = 0; for(;!macro(once);macro(once)=1)
|
||||
|
||||
#if is(cl)
|
||||
#define __thread __declspec(thread)
|
||||
#elif is(tcc) && is(win32)
|
||||
#define __thread __declspec(thread) // compiles fine, but does not work apparently
|
||||
#define __thread __declspec(thread) // compiles fine apparently, but does not work
|
||||
#elif is(tcc)
|
||||
#define __thread
|
||||
#endif
|
||||
|
@ -184,6 +187,44 @@
|
|||
// typedef union vec2 { float X,Y; }; vec2 a = {0,1}, b = vec2(0,1);
|
||||
#define C_CAST(type, ...) ( ifdef(c,(type),type) { __VA_ARGS__ } )
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// autorun initializers for C
|
||||
// - rlyeh, public domain
|
||||
//
|
||||
// note: based on code by Joe Lowe (public domain).
|
||||
// note: XIU for C initializers, XCU for C++ initializers, XTU for C deinitializers
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define AUTORUN \
|
||||
static void AUTORUN_U(f)(void); \
|
||||
static const int AUTORUN_J(AUTORUN_U(f),__1) = (AUTORUN_U(f)(), 1); \
|
||||
static void AUTORUN_U(f)(void)
|
||||
#elif defined _MSC_VER && !defined(__clang__) // cl, but not clang-cl
|
||||
#define AUTORUN \
|
||||
static void AUTORUN_U(f)(void); \
|
||||
static int AUTORUN_J(AUTORUN_U(f),__1) (){ AUTORUN_U(f)(); return 0; } \
|
||||
__pragma(section(".CRT$XIU", long, read)) \
|
||||
__declspec(allocate(".CRT$XIU")) \
|
||||
static int(* AUTORUN_J(AUTORUN_U(f),__2) )() = AUTORUN_J(AUTORUN_U(f),__1); \
|
||||
static void AUTORUN_U(f)(void)
|
||||
#else // gcc,tcc,clang,clang-cl...
|
||||
#define AUTORUN \
|
||||
__attribute__((constructor)) \
|
||||
static void AUTORUN_U(f)(void)
|
||||
#endif
|
||||
|
||||
// join + unique macro utils
|
||||
|
||||
#define AUTORUN_j(a, b) a##b
|
||||
#define AUTORUN_J(a, b) AUTORUN_j(a, b)
|
||||
#define AUTORUN_U(x) AUTORUN_J(x, __LINE__)
|
||||
|
||||
#if 0 // autorun demo
|
||||
void byebye(void) { puts("seen after main()"); }
|
||||
AUTORUN { puts("seen before main()"); }
|
||||
AUTORUN { puts("seen before main() too"); atexit( byebye ); }
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// build info
|
||||
|
||||
|
|
|
@ -120,7 +120,7 @@ static __thread unsigned array_n_;
|
|||
} while(0)
|
||||
|
||||
#define array_swapback_and_pop(t, i) do { /*may alter ordering*/ \
|
||||
memmove( &(t)[i], &(t)[array_count(t) - 1], sizeof(0[t])); \
|
||||
memcpy( &(t)[i], &(t)[array_count(t) - 1], sizeof(0[t])); \
|
||||
array_pop(t); \
|
||||
} while(0)
|
||||
|
||||
|
|
|
@ -15,8 +15,8 @@ vec3 editor_pick(float mouse_x, float mouse_y) {
|
|||
return out;
|
||||
#else
|
||||
// unproject 2d coord as 3d coord
|
||||
vec2 dpi = ifdef(osx, window_dpi(), vec2(1,1));
|
||||
camera_t *camera = camera_get_active();
|
||||
vec2 dpi = window_dpi();
|
||||
float x = (2.0f * mouse_x) / (dpi.x * window_width()) - 1.0f;
|
||||
float y = 1.0f - (2.0f * mouse_y) / (dpi.y * window_height());
|
||||
float z = 1.0f;
|
||||
|
|
|
@ -200,18 +200,17 @@ char *ext = strrchr(base, '.'); //if (ext) ext[0] = '\0'; // remove all extensio
|
|||
}
|
||||
return va("%s", buffer);
|
||||
}
|
||||
const char** file_list(const char *cwd, const char *masks) {
|
||||
ASSERT(strend(cwd, "/"), "Error: dirs like '%s' must end with slash", cwd);
|
||||
|
||||
static __thread array(char*) list = 0;
|
||||
const char *arg0 = cwd; // app_path();
|
||||
int larg0 = strlen(arg0);
|
||||
const char** file_list(const char *cwds, const char *masks) {
|
||||
static __thread array(char*) list = 0; // @fixme: should we add 16 slots in here similar to what we do in va() ?
|
||||
|
||||
for( int i = 0; i < array_count(list); ++i ) {
|
||||
FREE(list[i]);
|
||||
}
|
||||
array_resize(list, 0);//array_free(list);
|
||||
|
||||
for each_substring(cwds,";",cwd) {
|
||||
ASSERT(strend(cwd, "/"), "Error: dirs like '%s' must end with slash", cwd);
|
||||
|
||||
dir *d = dir_open(cwd, strstr(masks,"**") ? "r" : "");
|
||||
if( d ) {
|
||||
for( int i = 0; i < dir_count(d); ++i ) {
|
||||
|
@ -235,6 +234,7 @@ const char** file_list(const char *cwd, const char *masks) {
|
|||
}
|
||||
dir_close(d);
|
||||
}
|
||||
}
|
||||
|
||||
array_push(list, 0); // terminator
|
||||
return (const char**)list;
|
||||
|
@ -590,10 +590,7 @@ void vfs_reload() {
|
|||
// if( vfs_mount(va("%s.%02x", app, i)) ) continue;
|
||||
} */
|
||||
// faster way
|
||||
for( const char **file = file_list("./","*.zip"); *file; ++file) {
|
||||
// PRINTF("VFS mounted: %s\n", *file);
|
||||
vfs_mount(*file);
|
||||
}
|
||||
for( const char **file = file_list("./","*.zip"); *file; ++file) vfs_mount(*file);
|
||||
#endif
|
||||
|
||||
// vfs_resolve() will use these art_folder locations as hints when cook-on-demand is in progress.
|
||||
|
|
|
@ -13,44 +13,6 @@ API int semvercmp( int v1, int v2 );
|
|||
#define SEMVERCMP(v1,v2) (((v1) & 0110) - ((v2) & 0110))
|
||||
#define SEMVERFMT "%03o"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// autorun initializers for C
|
||||
// - rlyeh, public domain
|
||||
//
|
||||
// note: based on code by Joe Lowe (public domain).
|
||||
// note: XIU for C initializers, XCU for C++ initializers, XTU for C deinitializers
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define AUTORUN \
|
||||
static void AUTORUN_U(f)(void); \
|
||||
static const int AUTORUN_J(AUTORUN_U(f),__1) = (AUTORUN_U(f)(), 1); \
|
||||
static void AUTORUN_U(f)(void)
|
||||
#elif _MSC_VER
|
||||
#define AUTORUN \
|
||||
static void AUTORUN_U(f)(void); \
|
||||
static int AUTORUN_J(AUTORUN_U(f),__1) (){ AUTORUN_U(f)(); return 0; } \
|
||||
__pragma(section(".CRT$XIU", long, read)) \
|
||||
__declspec(allocate(".CRT$XIU")) \
|
||||
static int(* AUTORUN_J(AUTORUN_U(f),__2) )() = AUTORUN_J(AUTORUN_U(f),__1); \
|
||||
static void AUTORUN_U(f)(void)
|
||||
#else
|
||||
#define AUTORUN \
|
||||
__attribute__((constructor)) \
|
||||
static void AUTORUN_U(f)(void)
|
||||
#endif
|
||||
|
||||
// join + unique macro utils
|
||||
|
||||
#define AUTORUN_j(a, b) a##b
|
||||
#define AUTORUN_J(a, b) AUTORUN_j(a, b)
|
||||
#define AUTORUN_U(x) AUTORUN_J(x, __LINE__)
|
||||
|
||||
#if 0 // autorun demo
|
||||
void byebye(void) { puts("seen after main()"); }
|
||||
AUTORUN { puts("seen before main()"); }
|
||||
AUTORUN { puts("seen before main() too"); atexit( byebye ); }
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// storage types. refer to vec2i/3i, vec2/3/4 if you plan to do math operations
|
||||
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
// C reflection: enums, functions, structs, members and anotations.
|
||||
// - rlyeh, public domain
|
||||
//
|
||||
// @todo: nested structs? pointers in members?
|
||||
// @todo: declare TYPEDEF(vec3, float[3]), TYPEDEF(mat4, vec4[4]/*float[16]*/)
|
||||
|
||||
static map(unsigned, reflected_t) reflects;
|
||||
static map(unsigned, array(reflected_t)) members;
|
||||
|
||||
void reflected_printf(reflected_t *r) {
|
||||
printf("id:%u objtype:%u sz:%u name:%s info:%s addr:%p parent:%u type:%s",
|
||||
r->id, r->objtype, r->sz, r->name ? r->name : "", r->info ? r->info : "", r->addr, r->parent, r->type ? r->type : "");
|
||||
}
|
||||
void reflected_printf_all() {
|
||||
for each_map_ptr(reflects, unsigned, k, reflected_t, p) {
|
||||
reflected_printf(p);
|
||||
puts("");
|
||||
}
|
||||
}
|
||||
|
||||
void type_inscribe(const char *TY,unsigned TYid,unsigned TYsz,const char *infos) {
|
||||
if(!reflects) map_init_int(reflects);
|
||||
map_find_or_add(reflects, TYid, ((reflected_t){TYid, 0, TYsz, TY, infos}));
|
||||
}
|
||||
void enum_inscribe(const char *E,unsigned Eid,unsigned Eval,const char *infos) {
|
||||
if(!reflects) map_init_int(reflects);
|
||||
map_find_or_add(reflects, Eid, ((reflected_t){Eid,0, Eval, E,infos}));
|
||||
}
|
||||
unsigned enum_find(const char *E) {
|
||||
return map_find(reflects, intern(E))->sz;
|
||||
}
|
||||
void function_inscribe(const char *F,unsigned Fid,void *func,const char *infos) {
|
||||
if(!reflects) map_init_int(reflects);
|
||||
map_find_or_add(reflects, Fid, ((reflected_t){Fid,0, 0, F,infos, func}));
|
||||
reflected_t *found = map_find(reflects,Fid);
|
||||
}
|
||||
void *function_find(const char *F) {
|
||||
return map_find(reflects, intern(F))->addr;
|
||||
}
|
||||
void struct_inscribe(const char *T,unsigned Tid,unsigned Tsz,unsigned OBJTYPEid, const char *infos) {
|
||||
if(!reflects) map_init_int(reflects);
|
||||
map_find_or_add(reflects, Tid, ((reflected_t){Tid, OBJTYPEid, Tsz, T, infos}));
|
||||
}
|
||||
void member_inscribe(unsigned Tid, const char *M,unsigned Mid,unsigned Msz, const char *infos, const char *type) {
|
||||
if(!reflects) map_init_int(reflects);
|
||||
map_find_or_add(reflects, (Mid<<16)|Tid, ((reflected_t){Mid, 0, Msz, M, infos, NULL, Tid, type }));
|
||||
// add member separately as well
|
||||
if(!members) map_init_int(members);
|
||||
array(reflected_t) *found = map_find_or_add(members, Tid, 0);
|
||||
array_push(*found, ((reflected_t){Mid, 0, Msz, M, infos, NULL, Tid, type }));
|
||||
}
|
||||
reflected_t member_find(const char *T, const char *M) {
|
||||
return *map_find(reflects, (intern(M)<<16)|intern(T));
|
||||
}
|
||||
void *member_findptr(void *obj, const char *T, const char *M) {
|
||||
return (char*)obj + member_find(T,M).sz;
|
||||
}
|
||||
array(reflected_t) members_find(const char *T) {
|
||||
return *map_find(members, intern(T));
|
||||
}
|
||||
|
||||
// -- tests
|
||||
|
||||
enum {
|
||||
MYVALUE0 = 0,
|
||||
MYVALUE1,
|
||||
MYVALUE2,
|
||||
MYVALUEA = 123,
|
||||
};
|
||||
|
||||
typedef struct MyVec4 {
|
||||
float x,y,z,w;
|
||||
} MyVec4;
|
||||
|
||||
ifdef(objapi, enum { OBJTYPE_MyVec4 = 0x100 });
|
||||
|
||||
AUTOTEST {
|
||||
// register structs, enums and functions
|
||||
|
||||
STRUCT( MyVec4, float, x, "Right" );
|
||||
STRUCT( MyVec4, float, y, "Forward" );
|
||||
STRUCT( MyVec4, float, z, "Up" );
|
||||
STRUCT( MyVec4, float, w, "W" );
|
||||
|
||||
ENUM( MYVALUE0, "bla bla #0" );
|
||||
ENUM( MYVALUE1, "bla bla #1" );
|
||||
ENUM( MYVALUE2, "bla bla #2" );
|
||||
ENUM( MYVALUEA, "bla bla (A)" );
|
||||
|
||||
FUNCTION( puts, "handy function that I use a lot" );
|
||||
FUNCTION( printf, "handy function that I use a lot" );
|
||||
|
||||
// verify some reflected infos
|
||||
|
||||
test( function_find("puts") == puts );
|
||||
test( function_find("printf") == printf );
|
||||
|
||||
test( enum_find("MYVALUE0") == MYVALUE0 );
|
||||
test( enum_find("MYVALUE1") == MYVALUE1 );
|
||||
test( enum_find("MYVALUE2") == MYVALUE2 );
|
||||
test( enum_find("MYVALUEA") == MYVALUEA );
|
||||
|
||||
// iterate reflected struct
|
||||
for each_member("MyVec4", R) {
|
||||
printf("+%s MyVec4.%s // %s\n", R->type, R->name, R->info);
|
||||
}
|
||||
|
||||
reflected_printf_all();
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
// C reflection: enums, functions, structs, members and anotations.
|
||||
// - rlyeh, public domain
|
||||
//
|
||||
// @todo: nested structs? pointers in members?
|
||||
// @todo: declare TYPEDEF(vec3, float[3]), TYPEDEF(mat4, vec4[4]/*float[16]*/)
|
||||
|
||||
#define ifdef_objapi(T,...) __VA_ARGS__
|
||||
|
||||
typedef struct reflected_t {
|
||||
unsigned id, objtype;
|
||||
unsigned sz;
|
||||
const char *name;
|
||||
const char *info;
|
||||
void *addr;
|
||||
unsigned parent;
|
||||
const char *type;
|
||||
} reflected_t;
|
||||
|
||||
// inscribe api
|
||||
|
||||
#define ENUM(V, value_annotations) \
|
||||
enum_inscribe(#V,intern(#V),V, value_annotations)
|
||||
|
||||
#define FUNCTION(F, function_annotations) \
|
||||
function_inscribe(#F,intern(#F),(void*)F, function_annotations)
|
||||
|
||||
#define STRUCT(T, type, member, member_annotations) \
|
||||
struct_inscribe(#T,intern(#T),sizeof(T),ifdef(objapi,OBJTYPE_##T,0),NULL), \
|
||||
type_inscribe(#type,intern(#type),sizeof(((T){0}).member),member_annotations), \
|
||||
member_inscribe(intern(#T), #member,intern(#member),(uintptr_t)&((T*)0)->member, member_annotations, #type )
|
||||
|
||||
// find api
|
||||
|
||||
API unsigned enum_find(const char *E);
|
||||
API void * function_find(const char *F);
|
||||
|
||||
API reflected_t member_find(const char *T, const char *M); /// find specific member
|
||||
API void * member_findptr(void *obj, const char *T, const char *M);
|
||||
API array(reflected_t) members_find(const char *T);
|
||||
|
||||
// iterate members in a struct
|
||||
|
||||
#define each_member(T,R) \
|
||||
(array(reflected_t)*found_ = map_find(members, intern(T)); found_; found_ = 0) \
|
||||
for(int it_ = 0, end_ = array_count(*found_); it_ != end_; ++it_ ) \
|
||||
for(reflected_t *R = (*found_)+it_; R; R = 0 )
|
||||
|
||||
// private api, still exposed
|
||||
|
||||
API void type_inscribe(const char *TY,unsigned TYid,unsigned TYsz,const char *infos);
|
||||
API void enum_inscribe(const char *E,unsigned Eid,unsigned Eval,const char *infos);
|
||||
API void struct_inscribe(const char *T,unsigned Tid,unsigned Tsz,unsigned OBJTYPEid, const char *infos);
|
||||
API void member_inscribe(unsigned Tid, const char *M,unsigned Mid,unsigned Msz, const char *infos, const char *type);
|
||||
API void function_inscribe(const char *F,unsigned Fid,void *func,const char *infos);
|
||||
|
||||
API void reflected_printf(reflected_t *r);
|
||||
API void reflected_printf_all();
|
|
@ -709,18 +709,14 @@ void die(const char *message) {
|
|||
// ----------------------------------------------------------------------------
|
||||
// logger
|
||||
|
||||
unsigned determine_color_from_text(const char *text) {
|
||||
/**/ if( strstri(text, "fail") || strstri(text, "error") ) return RED;
|
||||
else if( strstri(text, "warn") || strstri(text, "not found") ) return YELLOW;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//static int __thread _thread_id;
|
||||
//#define PRINTF(...) (printf("%03d %07.3fs|%-16s|", (((unsigned)(uintptr_t)&_thread_id)>>8) % 1000, time_ss(), __FUNCTION__), printf(__VA_ARGS__), printf("%s", 1[#__VA_ARGS__] == '!' ? callstack(+48) : "")) // verbose logger
|
||||
|
||||
int (PRINTF)(const char *text, const char *stack, const char *file, int line, const char *function) {
|
||||
double secs = time_ss();
|
||||
uint32_t color = /*errno ? RED :*/ determine_color_from_text(text); // errno = 0;
|
||||
uint32_t color = 0;
|
||||
/**/ if( strstri(text, "fail") || strstri(text, "error") ) color = RED;
|
||||
else if( strstri(text, "warn") || strstri(text, "not found") ) color = YELLOW;
|
||||
#if is(cl)
|
||||
char *slash = strrchr(file, '\\'); if(slash) file = slash + 1;
|
||||
#endif
|
||||
|
@ -862,5 +858,5 @@ static void test_exit(void) { fprintf(stderr, "%d/%d tests passed\n", test_oks,
|
|||
int (test)(const char *file, int line, const char *expr, bool result) {
|
||||
test_once = test_once || !(atexit)(test_exit);
|
||||
test_oks += result, test_errs += !result;
|
||||
return (result || fprintf(stderr, "(Test `%s` failed %s:%d)\n", expr, file, line) );
|
||||
return (result || (tty_color(RED), fprintf(stderr, "(Test `%s` failed %s:%d)\n", expr, file, line), tty_color(0), 0) );
|
||||
}
|
||||
|
|
|
@ -63,4 +63,12 @@ API int (PRINTF)(const char *text, const char *stack, const char *file, int line
|
|||
|
||||
#define test(expr) test(__FILE__,__LINE__,#expr,!!(expr))
|
||||
API int (test)(const char *file, int line, const char *expr, bool result);
|
||||
// AUTORUN { test(1<2); }
|
||||
|
||||
#if ENABLE_TESTS
|
||||
#define AUTOTEST AUTORUN
|
||||
#else
|
||||
#define AUTOTEST static void concat(concat(concat(disabled_test_, __LINE__), _), __COUNTER__)()
|
||||
#endif
|
||||
|
||||
// AUTOTEST { test(1<2); }
|
||||
|
||||
|
|
|
@ -813,12 +813,6 @@ double window_time() {
|
|||
double window_delta() {
|
||||
return dt;
|
||||
}
|
||||
vec2 window_dpi() {
|
||||
float x=0.0f;
|
||||
float y=0.0f;
|
||||
glfwGetMonitorContentScale(glfwGetPrimaryMonitor(), &x, &y);
|
||||
return vec2(x,y);
|
||||
}
|
||||
|
||||
double window_fps() {
|
||||
return fps;
|
||||
|
@ -894,6 +888,12 @@ int window_record(const char *outfile_mp4) {
|
|||
return record_active();
|
||||
}
|
||||
|
||||
vec2 window_dpi() {
|
||||
float x=0.0f;
|
||||
float y=0.0f;
|
||||
glfwGetMonitorContentScale(glfwGetPrimaryMonitor(), &x, &y);
|
||||
return vec2(x,y);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// fullscreen
|
||||
|
|
|
@ -45,7 +45,6 @@ API char* window_stats();
|
|||
API uint64_t window_frame();
|
||||
API int window_width();
|
||||
API int window_height();
|
||||
API vec2 window_dpi();
|
||||
API double window_time();
|
||||
API double window_delta();
|
||||
|
||||
|
@ -79,6 +78,8 @@ API void window_fps_unlock();
|
|||
API void window_screenshot(const char* outfile_png); // , bool record_cursor
|
||||
API int window_record(const char *outfile_mp4); // , bool record_cursor
|
||||
|
||||
API vec2 window_dpi();
|
||||
|
||||
enum CURSOR_SHAPES {
|
||||
CURSOR_NONE,
|
||||
CURSOR_HW_ARROW, // default
|
||||
|
|
250831
engine/v4k
250831
engine/v4k
File diff suppressed because it is too large
Load Diff
326
engine/v4k.c
326
engine/v4k.c
|
@ -5175,18 +5175,17 @@ char *ext = strrchr(base, '.'); //if (ext) ext[0] = '\0'; // remove all extensio
|
|||
}
|
||||
return va("%s", buffer);
|
||||
}
|
||||
const char** file_list(const char *cwd, const char *masks) {
|
||||
ASSERT(strend(cwd, "/"), "Error: dirs like '%s' must end with slash", cwd);
|
||||
|
||||
static __thread array(char*) list = 0;
|
||||
const char *arg0 = cwd; // app_path();
|
||||
int larg0 = strlen(arg0);
|
||||
const char** file_list(const char *cwds, const char *masks) {
|
||||
static __thread array(char*) list = 0; // @fixme: should we add 16 slots in here similar to what we do in va() ?
|
||||
|
||||
for( int i = 0; i < array_count(list); ++i ) {
|
||||
FREE(list[i]);
|
||||
}
|
||||
array_resize(list, 0);//array_free(list);
|
||||
|
||||
for each_substring(cwds,";",cwd) {
|
||||
ASSERT(strend(cwd, "/"), "Error: dirs like '%s' must end with slash", cwd);
|
||||
|
||||
dir *d = dir_open(cwd, strstr(masks,"**") ? "r" : "");
|
||||
if( d ) {
|
||||
for( int i = 0; i < dir_count(d); ++i ) {
|
||||
|
@ -5210,6 +5209,7 @@ const char** file_list(const char *cwd, const char *masks) {
|
|||
}
|
||||
dir_close(d);
|
||||
}
|
||||
}
|
||||
|
||||
array_push(list, 0); // terminator
|
||||
return (const char**)list;
|
||||
|
@ -5565,10 +5565,7 @@ void vfs_reload() {
|
|||
// if( vfs_mount(va("%s.%02x", app, i)) ) continue;
|
||||
} */
|
||||
// faster way
|
||||
for( const char **file = file_list("./","*.zip"); *file; ++file) {
|
||||
// PRINTF("VFS mounted: %s\n", *file);
|
||||
vfs_mount(*file);
|
||||
}
|
||||
for( const char **file = file_list("./","*.zip"); *file; ++file) vfs_mount(*file);
|
||||
#endif
|
||||
|
||||
// vfs_resolve() will use these art_folder locations as hints when cook-on-demand is in progress.
|
||||
|
@ -12719,6 +12716,118 @@ AUTORUN {
|
|||
#endif
|
||||
#line 0
|
||||
|
||||
#line 1 "v4k_reflect.c"
|
||||
// C reflection: enums, functions, structs, members and anotations.
|
||||
// - rlyeh, public domain
|
||||
//
|
||||
// @todo: nested structs? pointers in members?
|
||||
// @todo: declare TYPEDEF(vec3, float[3]), TYPEDEF(mat4, vec4[4]/*float[16]*/)
|
||||
|
||||
static map(unsigned, reflected_t) reflects;
|
||||
static map(unsigned, array(reflected_t)) members;
|
||||
|
||||
void reflected_printf(reflected_t *r) {
|
||||
printf("id:%u objtype:%u sz:%u name:%s info:%s addr:%p parent:%u type:%s",
|
||||
r->id, r->objtype, r->sz, r->name ? r->name : "", r->info ? r->info : "", r->addr, r->parent, r->type ? r->type : "");
|
||||
}
|
||||
void reflected_printf_all() {
|
||||
for each_map_ptr(reflects, unsigned, k, reflected_t, p) {
|
||||
reflected_printf(p);
|
||||
puts("");
|
||||
}
|
||||
}
|
||||
|
||||
void type_inscribe(const char *TY,unsigned TYid,unsigned TYsz,const char *infos) {
|
||||
if(!reflects) map_init_int(reflects);
|
||||
map_find_or_add(reflects, TYid, ((reflected_t){TYid, 0, TYsz, TY, infos}));
|
||||
}
|
||||
void enum_inscribe(const char *E,unsigned Eid,unsigned Eval,const char *infos) {
|
||||
if(!reflects) map_init_int(reflects);
|
||||
map_find_or_add(reflects, Eid, ((reflected_t){Eid,0, Eval, E,infos}));
|
||||
}
|
||||
unsigned enum_find(const char *E) {
|
||||
return map_find(reflects, intern(E))->sz;
|
||||
}
|
||||
void function_inscribe(const char *F,unsigned Fid,void *func,const char *infos) {
|
||||
if(!reflects) map_init_int(reflects);
|
||||
map_find_or_add(reflects, Fid, ((reflected_t){Fid,0, 0, F,infos, func}));
|
||||
reflected_t *found = map_find(reflects,Fid);
|
||||
}
|
||||
void *function_find(const char *F) {
|
||||
return map_find(reflects, intern(F))->addr;
|
||||
}
|
||||
void struct_inscribe(const char *T,unsigned Tid,unsigned Tsz,unsigned OBJTYPEid, const char *infos) {
|
||||
if(!reflects) map_init_int(reflects);
|
||||
map_find_or_add(reflects, Tid, ((reflected_t){Tid, OBJTYPEid, Tsz, T, infos}));
|
||||
}
|
||||
void member_inscribe(unsigned Tid, const char *M,unsigned Mid,unsigned Msz, const char *infos, const char *type) {
|
||||
if(!reflects) map_init_int(reflects);
|
||||
map_find_or_add(reflects, (Mid<<16)|Tid, ((reflected_t){Mid, 0, Msz, M, infos, NULL, Tid, type }));
|
||||
// add member separately as well
|
||||
if(!members) map_init_int(members);
|
||||
array(reflected_t) *found = map_find_or_add(members, Tid, 0);
|
||||
array_push(*found, ((reflected_t){Mid, 0, Msz, M, infos, NULL, Tid, type }));
|
||||
}
|
||||
reflected_t member_find(const char *T, const char *M) {
|
||||
return *map_find(reflects, (intern(M)<<16)|intern(T));
|
||||
}
|
||||
void *member_findptr(void *obj, const char *T, const char *M) {
|
||||
return (char*)obj + member_find(T,M).sz;
|
||||
}
|
||||
array(reflected_t) members_find(const char *T) {
|
||||
return *map_find(members, intern(T));
|
||||
}
|
||||
|
||||
// -- tests
|
||||
|
||||
enum {
|
||||
MYVALUE0 = 0,
|
||||
MYVALUE1,
|
||||
MYVALUE2,
|
||||
MYVALUEA = 123,
|
||||
};
|
||||
|
||||
typedef struct MyVec4 {
|
||||
float x,y,z,w;
|
||||
} MyVec4;
|
||||
|
||||
ifdef(objapi, enum { OBJTYPE_MyVec4 = 0x100 });
|
||||
|
||||
AUTOTEST {
|
||||
// register structs, enums and functions
|
||||
|
||||
STRUCT( MyVec4, float, x, "Right" );
|
||||
STRUCT( MyVec4, float, y, "Forward" );
|
||||
STRUCT( MyVec4, float, z, "Up" );
|
||||
STRUCT( MyVec4, float, w, "W" );
|
||||
|
||||
ENUM( MYVALUE0, "bla bla #0" );
|
||||
ENUM( MYVALUE1, "bla bla #1" );
|
||||
ENUM( MYVALUE2, "bla bla #2" );
|
||||
ENUM( MYVALUEA, "bla bla (A)" );
|
||||
|
||||
FUNCTION( puts, "handy function that I use a lot" );
|
||||
FUNCTION( printf, "handy function that I use a lot" );
|
||||
|
||||
// verify some reflected infos
|
||||
|
||||
test( function_find("puts") == puts );
|
||||
test( function_find("printf") == printf );
|
||||
|
||||
test( enum_find("MYVALUE0") == MYVALUE0 );
|
||||
test( enum_find("MYVALUE1") == MYVALUE1 );
|
||||
test( enum_find("MYVALUE2") == MYVALUE2 );
|
||||
test( enum_find("MYVALUEA") == MYVALUEA );
|
||||
|
||||
// iterate reflected struct
|
||||
for each_member("MyVec4", R) {
|
||||
printf("+%s MyVec4.%s // %s\n", R->type, R->name, R->info);
|
||||
}
|
||||
|
||||
reflected_printf_all();
|
||||
}
|
||||
#line 0
|
||||
|
||||
#line 1 "v4k_render.c"
|
||||
// -----------------------------------------------------------------------------
|
||||
// opengl
|
||||
|
@ -20051,18 +20160,14 @@ void die(const char *message) {
|
|||
// ----------------------------------------------------------------------------
|
||||
// logger
|
||||
|
||||
unsigned determine_color_from_text(const char *text) {
|
||||
/**/ if( strstri(text, "fail") || strstri(text, "error") ) return RED;
|
||||
else if( strstri(text, "warn") || strstri(text, "not found") ) return YELLOW;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//static int __thread _thread_id;
|
||||
//#define PRINTF(...) (printf("%03d %07.3fs|%-16s|", (((unsigned)(uintptr_t)&_thread_id)>>8) % 1000, time_ss(), __FUNCTION__), printf(__VA_ARGS__), printf("%s", 1[#__VA_ARGS__] == '!' ? callstack(+48) : "")) // verbose logger
|
||||
|
||||
int (PRINTF)(const char *text, const char *stack, const char *file, int line, const char *function) {
|
||||
double secs = time_ss();
|
||||
uint32_t color = /*errno ? RED :*/ determine_color_from_text(text); // errno = 0;
|
||||
uint32_t color = 0;
|
||||
/**/ if( strstri(text, "fail") || strstri(text, "error") ) color = RED;
|
||||
else if( strstri(text, "warn") || strstri(text, "not found") ) color = YELLOW;
|
||||
#if is(cl)
|
||||
char *slash = strrchr(file, '\\'); if(slash) file = slash + 1;
|
||||
#endif
|
||||
|
@ -20204,7 +20309,7 @@ static void test_exit(void) { fprintf(stderr, "%d/%d tests passed\n", test_oks,
|
|||
int (test)(const char *file, int line, const char *expr, bool result) {
|
||||
test_once = test_once || !(atexit)(test_exit);
|
||||
test_oks += result, test_errs += !result;
|
||||
return (result || fprintf(stderr, "(Test `%s` failed %s:%d)\n", expr, file, line) );
|
||||
return (result || (tty_color(RED), fprintf(stderr, "(Test `%s` failed %s:%d)\n", expr, file, line), tty_color(0), 0) );
|
||||
}
|
||||
#line 0
|
||||
|
||||
|
@ -23944,12 +24049,6 @@ double window_time() {
|
|||
double window_delta() {
|
||||
return dt;
|
||||
}
|
||||
vec2 window_dpi() {
|
||||
float x=0.0f;
|
||||
float y=0.0f;
|
||||
glfwGetMonitorContentScale(glfwGetPrimaryMonitor(), &x, &y);
|
||||
return vec2(x,y);
|
||||
}
|
||||
|
||||
double window_fps() {
|
||||
return fps;
|
||||
|
@ -24025,6 +24124,12 @@ int window_record(const char *outfile_mp4) {
|
|||
return record_active();
|
||||
}
|
||||
|
||||
vec2 window_dpi() {
|
||||
float x=0.0f;
|
||||
float y=0.0f;
|
||||
glfwGetMonitorContentScale(glfwGetPrimaryMonitor(), &x, &y);
|
||||
return vec2(x,y);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// fullscreen
|
||||
|
@ -24884,6 +24989,177 @@ int pathfind_astar(int width, int height, const unsigned* map, vec2i src, vec2i
|
|||
#line 0
|
||||
|
||||
#line 1 "v4k_bt.c"
|
||||
// Behavior trees: decision planning and decision making.
|
||||
// Supersedes finite state-machines (FSM) and hierarchical finite state-machines (HFSM).
|
||||
// - rlyeh, public domain.
|
||||
//
|
||||
// [ref] https://outforafight.wordpress.com/2014/07/15/behaviour-behavior-trees-for-ai-dudes-part-1/
|
||||
// [ref] https://www.gameaipro.com/GameAIPro/GameAIPro_Chapter06_The_Behavior_Tree_Starter_Kit.pdf
|
||||
// [ref] https://gitlab.com/NotYetGames/DlgSystem/-/wikis/Dialogue-Manual
|
||||
// [ref] https://towardsdatascience.com/designing-ai-agents-behaviors-with-behavior-trees-b28aa1c3cf8a
|
||||
// [ref] https://docs.nvidia.com/isaac/packages/behavior_tree/doc/behavior_trees.html
|
||||
// [ref] https://docs.unrealengine.com/4.26/en-US/InteractiveExperiences/ArtificialIntelligence/BehaviorTrees/
|
||||
// [ref] gdc ChampandardDaweHernandezCerpa_BehaviorTrees.pdf @todo debugging
|
||||
// [ref] https://docs.cocos.com/cocos2d-x/manual/en/actions/
|
||||
|
||||
// The nodes in a behavior tree can be broadly categorized into three main types: control nodes, task nodes, and decorator nodes. Here is a brief description of each category:
|
||||
|
||||
// Control Nodes: Control nodes are used to control the flow of the behavior tree. They determine the order in which child nodes are executed and how the results of those nodes are combined. They are usually parent nodes.
|
||||
// Action Nodes: Action nodes are used to perform specific actions or tasks within the behavior tree. They can include actions such as moving, attacking, or interacting with objects in the game world. They are usually leaf nodes.
|
||||
// Decorator Nodes: Decorator nodes are used to modify the behavior of child nodes in some way. They can be used to repeat child nodes, invert the result of a child node, or add a cooldown period between executions. They are usually located between control and action nodes.
|
||||
|
||||
// --- VARIABLES
|
||||
|
||||
// Key Prefixes:
|
||||
// {visiblity:(l)ocal,s(q)uad,(r)ace,(f)action,(g)lobal}
|
||||
// [persistence:(t)emp,(u)serdata,(s)avegame,(c)loud] + '_' + name.
|
||||
// [persistence:(tmp),(usr)data,(sav)egame,(net)cloud] + '_' + name.
|
||||
|
||||
// Ie, l_health = 123.4, gsav_player = "john"
|
||||
|
||||
// --- ACTION NODES
|
||||
|
||||
// [ ] * Actions/Behaviors have a common structure:
|
||||
// [ ] - Entry point (function name) for a C call or Lua script.
|
||||
// [ ] * Status:
|
||||
// [ ] - Uninitialized (never run)
|
||||
// [ ] - Running (in progress)
|
||||
// [ ] - Suspended (on hold till resumed)
|
||||
// [ ] - Success (finished and succeeded)
|
||||
// [ ] - Failure (finished and failed)
|
||||
// [ ] * Optional callbacks:
|
||||
// [ ] - on_enter
|
||||
// [ ] - on_leave
|
||||
// [ ] - on_success
|
||||
// [ ] - on_failure
|
||||
// [ ] - on_suspend
|
||||
// [ ] - on_resume
|
||||
|
||||
// [x] Action Node: This node performs a single action, such as moving to a specific location or attacking a target.
|
||||
// [ ] Blackboard Node: Node that reads and writes data to a shared memory space known as a blackboard. The blackboard can be used to store information that is relevant to multiple nodes in the behavior tree.
|
||||
// [ ] SetKey(keyVar,object)
|
||||
// [ ] HasKey(keyVar)
|
||||
// [ ] CompareKeys(keyVar1, operator < <= > >= == !=, keyVar2)
|
||||
// [ ] SetTags(names=blank,cooldownTime=inf,bIsCooldownAdditive=false)
|
||||
// [ ] HasTags(names=blank,bAllRequired=true)
|
||||
// [ ] PushToStack(keyVar,itemObj): creates a new stack if one doesn’t exist, and stores it in the passed variable name, and then pushes ‘item’ object onto it.
|
||||
// [ ] PopFromStack(keyVar,itemVar): pop pops an item off the stack, and stores it in the itemVar variable, failing if the stack is already empty.
|
||||
// [ ] IsEmptyStack(keyVar): checks if the stack passed is empty and returns success if it is, and failure if its not.
|
||||
// [ ] Communication Node: This is a type of action node that allows an AI agent to communicate with other agents or entities in the game world. The node takes an input specifying the message to be communicated and the recipient(s) of the message (wildmask,l/p/f/g prefixes). The node then sends the message to the designated recipient(s) and returns success when the communication is completed. This node can be useful for implementing behaviors that require the AI agent to coordinate with other agents or to convey information to the player. It could use a radius argument to specify the maximum allowed distance for the recipients.
|
||||
// [ ] Condition Node: A leaf node that checks a specific condition, such as the distance to an object, the presence of an enemy, or the status of a health bar.
|
||||
// [ ] Distance Condition Node: This is a type of condition node that evaluates whether an AI agent is within a specified distance of a target object or location. The node takes two inputs: the current position of the AI agent and the position of the target object or location. If the distance between the two is within a specified range, the node returns success. If the distance is outside of the specified range, the node returns failure. This node can be useful for implementing behaviors that require the AI agent to maintain a certain distance from a target, such as following or avoiding an object. Could use a flag to disambiguate between linear distance and path distance.
|
||||
// [ ] Query Node: This node checks a condition and returns success or failure based on the result. For example, a query node could check whether an enemy is within range or whether a door is locked.
|
||||
// [ ] Query Node: A type of decorator node that retrieves information from a database or external system, such as a web service or file system. The Query node can be used to retrieve data that is not available within the game engine, such as weather or traffic conditions.
|
||||
// [ ] A condition is made of:
|
||||
// [ ] - Optional [!] negate
|
||||
// [ ] - Mandatory Value1(Int/Flt/Bool/VarName/FuncName)
|
||||
// [ ] - Optional operator [< <= > >= == !=] and Value2(Int/Flt/Bool/VarName/FuncName)
|
||||
// [ ] AllConditions(...) : SUCCESS if ( empty array || all conditions met)
|
||||
// [ ] AnyConditions(...) : SUCCESS if (!empty array && one conditions met)
|
||||
|
||||
// --- DECORATOR NODES
|
||||
|
||||
// [ ] Cooldown Node: Decorator node that adds a cooldown period between the execution of a child node, preventing it from being executed again until the cooldown period has elapsed.
|
||||
// [x] Counter Node: Decorator node that limits the number of times that a child node can execute. For example, if the child node has executed a certain number of times, the Counter node will return a failure.
|
||||
// [x] Once Node: Decorator node that triggers a specified action when a condition is met for the first time. This can be useful for triggering a one-time event, such as when an AI agent discovers a hidden item or reaches a new area of the game world.
|
||||
// [x] Inverter Node: Decorator node that inverts the result of a child node, returning success if the child node fails and failure if the child node succeeds. This can be useful for negating the outcome of a particular behavior, such as avoiding a certain area of the game world.
|
||||
// [x] Repeater Node: Decorator node that repeats the execution of a child node a specified number of times or indefinitely.
|
||||
// [x] Repeat(times=inf): Runs child node given times. These are often used at the very base of the tree, to make the tree to run continuously.
|
||||
// [ ] RepeatIf(strong/weak condition): Runs child node as long as the conditions are met.
|
||||
// [ ] RepeatIfOk(times=inf): Runs child node if it succeedes, max given times.
|
||||
// [ ] RepeatIfFail(times=inf): Runs child node if it fails, max given times.
|
||||
// [ ] Branch Node: 2 children [0] for true, [1] for false
|
||||
// [ ] Resource Node: Decorator node that manages a shared resource, such as a limited supply of ammunition or a limited amount of processing power. The Resource node The node can be used to decide when to use or conserve the resource. For example, if the AI agent is low on ammunition, the node may decide to switch to a melee weapon or to retreat to a safer location. This node can be useful for implementing behaviors that require the AI agent to make strategic decisions about resource use.
|
||||
// [x] Result Node: Decorator node that tracks the result of a child action node and returns either success or failure depending on the outcome. This can be useful for determining whether an action was successful or not, and then adjusting the AI agent's behavior accordingly.
|
||||
// [x] Succeeder Node: Decorator node that always returns success, regardless of the result of its child node. This can be useful for ensuring that certain behaviors are always executed, regardless of the overall success or failure of the behavior tree.
|
||||
// [x] Success(): FAILURE becomes SUCCESS (TRUE).
|
||||
// [x] Failure(): SUCCESS becomes FAILURE (FALSE).
|
||||
// [ ] Throttle Node: Decorator node that limits the rate at which a child node can execute. For example, a Throttle node might ensure that an AI agent can only fire its weapon, or using a special ability, only a certain number of times per second.
|
||||
// [x] Delay Node: Decorator node that adds a delay to the execution of a child node. The delay might be configured to sleep before the execution, after the execution, or both.
|
||||
// [x] Defer Delay(duration_ss=1): Runs the child node, then sleeps for given seconds.
|
||||
// [ ] Ease(time,name): Clamps child time to [0,1] range, and applies easing function on it.
|
||||
// [ ] Dilate(Mul=1,Add=0): Dilates child time
|
||||
|
||||
// --- CONTROL NODES
|
||||
|
||||
// [x] Root Node: The topmost node in a behavior tree that represents the start of the decision-making process. Returns success if any of its child nodes suceedes.
|
||||
// [x] Root-Failure Node: Control node that only returns failure if all of its child nodes fail. This can be useful for ensuring that a behavior tree is not prematurely terminated if one or more child nodes fail.
|
||||
|
||||
// [ ] Event(name): When name event is raised, it suspends current tree and calls child. "Incoming projectile" -> Evade. Stimulus types: may be disabled by event, or autodisabled.
|
||||
// [ ] Raise(name): Triggers event name.
|
||||
|
||||
// [ ] Checkpoint Node: Control node that saves a state in memory and then continues the tree execution from that point the next time the tree is executed. It can be useful in situations where the behavior tree needs to be interrupted and resumed later.
|
||||
// [ ] Decision Making Node: Control node that implements a decision-making process for the AI agent. The node takes input specifying the available options for the AI agent and the criteria for evaluating each option. The node then evaluates each option based on the specified criteria and selects the best option. This node can be useful for implementing behaviors that require the AI agent to make strategic decisions, such as choosing a target or selecting a path through the game world.
|
||||
// [ ] Could be extended with GOAP if dynamically inserting the scores on each update then calling a Probability Selector Node (0.2,0.3,0.5)
|
||||
// [ ] https://cdn.cloudflare.steamstatic.com/apps/valve/2012/GDC2012_Ruskin_Elan_DynamicDialog.pdf
|
||||
// [ ] Evaluate Node / Recheck():
|
||||
// [ ] Actively rechecks all existing sub-conditions on a regular basis after having made decisions about them.
|
||||
// [ ] Use this feature to dynamically check for risks or opportunities in selected parts of the tree.
|
||||
// [ ] For example, interrupting a patrol with a search behavior if a disturbance is reported.
|
||||
// [ ] Interrupt Node: Control node that interrupts the execution of a lower-priority node when a higher-priority node needs to be executed. It can be useful for handling urgent tasks or emergency situations.
|
||||
// [ ] Monitor Node: Control node that continuously monitors a condition and triggers a specified action when the condition is met. For example, a Monitor node might monitor the distance between an AI agent and a target and trigger a "retreat" behavior when the distance falls below a certain threshold.
|
||||
// [ ] Observer Node: Control node that monitors the state of the game world and triggers a specified action when certain conditions are met. For example, an Observer node might trigger a "hide" behavior when an enemy is spotted.
|
||||
// [ ] Parallel Node: Control node that executes multiple child nodes simultaneously. The Parallel node continues to execute even if some of its child nodes fail.
|
||||
// [ ] Parallel All Node(required_successes=100%): Control node that executes multiple child nodes simultaneously. Returns false when first child fails, and aborts any other running tasks. Returns true if all its children succeed.
|
||||
// [ ] Parallel One Node(required_successes=1): Control node that executes multiple child nodes simultaneously. Returns true when first child suceedes, and aborts any other running tasks. Returns false if all its children fail.
|
||||
// [ ] Subsumption Node: Control node that allows multiple behaviors to be executed simultaneously, with higher-priority behaviors taking precedence over lower-priority ones. This can be useful for implementing complex, multi-level behaviors in autonomous systems.
|
||||
// [ ] Semaphore Node: Control node that blocks the execution of its child nodes until a certain condition is met. For example, a Semaphore node might block the execution of a behavior until a certain object is in range or a certain event occurs.
|
||||
// [ ] Semaphore Wait Node: Control node that blocks the execution of its child nodes until a resource becomes available. This can be useful for controlling access to shared resources such as a pathfinding system or a communication channel.
|
||||
// [ ] WaitTags(tags=blank,timeout_ss=inf): Stops execution of child node until the cooldown tag(s) do expire. May return earlier if timed out.
|
||||
// [ ] WaitEvent(name=blank,timeout_ss=inf): Stops execution of child node until event is raised. May return earlier if timed out.
|
||||
// [x] Sequence Node(reversed,iterator(From,To)): Control node that executes a series of child nodes in order. If any of the child nodes fail, the Sequence node stops executing and returns a failure.
|
||||
// [ ] Dynamic Sequence Node: Control node that dynamically changes the order in which its child nodes are executed based on certain conditions. For example, a Dynamic Sequence node might give higher priority to a child node that has recently failed or that is more likely to succeed.
|
||||
// [ ] Reverse(): iterates children in reversed order (Iterator:(-1,0) equivalent)
|
||||
// [ ] Iterator(from,to): allows negative indexing. increments if (abs from < abs to), decrements otherwise
|
||||
// [x] Selector Node: Control node that selects a child node to execute based on a predefined priority or set of conditions. The Selector node stops executing child nodes as soon as one of them succeeds.
|
||||
// [ ] Priority Selector Node: Control node that executes its child nodes in order of priority. If the first child node fails, it moves on to the next child node in the list until a successful node is found. This can be useful for implementing behaviors that require a specific order of execution, such as a patrol route or a search pattern.
|
||||
// [ ] Probability Selector Node: Control node that selects a child node to execute based on a probability distribution. For example, if there are three child nodes with probabilities of 0.2, 0.3, and 0.5, the Probability Selector node will execute the third child node 50% of the time.
|
||||
// [ ] Dynamic Selector Node: Control node that dynamically changes the order in which its child nodes are executed based on certain conditions. For example, a Dynamic Selector node might give higher priority to a child node that has recently succeeded or that is more likely to succeed.
|
||||
// [ ] Dynamic Selector Node: Control node that dynamically changes the order in which child nodes are executed based on certain conditions. For example, it may prioritize certain nodes when health is low, and other nodes when resources are scarce.
|
||||
// [ ] Dynamic Priority Node: Control node that dynamically changes the priority of its child nodes based on certain conditions. For example, if a child node is more likely to result in success, the Dynamic Priority node will give it a higher priority.
|
||||
// [ ] Weighted / Cover Selection Node: This is a type of selector node that evaluates the available cover options in the game world and selects the best cover position for the AI agent. The node takes input specifying the current position of the AI agent and the location of potential cover positions. The node then evaluates the cover positions based on criteria such as distance to enemy positions, line of sight, and available cover points, and selects the best option. This node can be useful for implementing behaviors that require the AI agent to take cover and avoid enemy fire.
|
||||
// [ ] Random Selector Node: Control node that selects a child node to execute at random. This can be useful for introducing variety and unpredictability into the behavior of an AI agent.
|
||||
// [ ] Random Weight / Stochastic Node(0.2,0.3,0.5): Control node that introduces randomness into the decision-making process of an AI agent. For example, a Stochastic node might randomly select a child node to execute, with the probability of each child node being proportional to its likelihood of success.
|
||||
// [ ] Break(bool): breaks parent sequence or selector. may return SUCCESS or FAILURE.
|
||||
// [ ] Hybrid Node: Control node that combines the functionality of multiple control nodes, such as a sequence node and a selector node. It can be useful for creating complex behavior patterns that require multiple control structures. The Hybrid Node has two modes of operation: strict mode and flexible mode. In strict mode, the child nodes are executed in a fixed order, similar to a Sequence Node. If any child node fails, the entire Hybrid Node fails. In flexible mode, the child nodes can be executed in any order, similar to a Selector Node. If any child node succeeds, the entire Hybrid Node succeeds. The Hybrid Node is often used when you need to create complex behavior patterns that require multiple control structures. For example, you might use a Hybrid Node to implement a search behavior where the AI agent first moves in a fixed direction for a certain distance (strict mode), but then switches to a more exploratory mode where it randomly explores nearby areas (flexible mode).
|
||||
// [ ] Subtree Node: Control node that calls another behavior tree as a subroutine. This is useful for breaking down complex behaviors into smaller, more manageable parts.
|
||||
// [ ] Call(name): Calls to the child node with the matching name within behavior tree. Returns FAILURE if name is invalid or if invoked behavior returns FAILURE.
|
||||
// [ ] Return(boolean): Exits current Call.
|
||||
// [ ] AttachTree(Name,bDetachAfterUse=true): Attaches subtree to main tree. When a NPC founds the actor, it attaches the behavior to the tree. Ie, Level specific content: Patrols, Initial setups, Story driven events, etc. Ie, DLCs: Behaviors are added to actors in the DLC level (enticers).
|
||||
// [ ] DetachTree(Name)
|
||||
// [ ] Switch Node: Control node that evaluates a condition and then selects one of several possible child nodes to execute based on the result of the condition.
|
||||
// [ ] Switch(name): Jumps to the child node with the matching name within behavior tree. If name is invalid will defer to Fallback child node.
|
||||
// [ ] Timeout Node: Control node that aborts the execution of its child node after a certain amount of time has elapsed. This can be useful for preventing an AI agent from getting stuck in a loop or waiting indefinitely for a particular event to occur.
|
||||
// [ ] TimeLimit(timeout_ss=inf): Give the child any amount of time to finish before it gets canceled. The timer is reset every time the node gains focus.
|
||||
// [ ] Timer Node: Control node which invokes its child node every XX seconds. The timer can repeat the action any given number of times, or indefinitely.
|
||||
|
||||
// ## Proposal -----------------------------------------------------------------------------
|
||||
|
||||
// BehaviorTrees as case-insensitive INI files. Then,
|
||||
// - INI -> C INTERPRETER, OR
|
||||
// - INI -> LUA TRANSPILER -> LUA BYTECODE -> LUA VM.
|
||||
|
||||
// ```ini
|
||||
// [bt]
|
||||
// recheck
|
||||
// sequence ;; Approach the player if seen!
|
||||
// conditions=IsPlayerVisible,
|
||||
// action=MoveTowardsPlayer
|
||||
// sequence ;; Attack the player if seen!
|
||||
// conditions=IsPlayerInRange,
|
||||
// repeat=3
|
||||
// action=FireAtPlayer
|
||||
// sequence ;; Search near last known position
|
||||
// conditions=HaveWeGotASuspectedLocation,
|
||||
// action=MoveToPlayersLastKnownPosition
|
||||
// action=LookAround
|
||||
// sequence ;; Randomly scanning nearby
|
||||
// action=MoveToRandomPosition
|
||||
// action=LookAround
|
||||
// event=IncomingProjectile
|
||||
// action=Evade
|
||||
// ```
|
||||
|
||||
map(char*, bt_func) binds;
|
||||
|
||||
void bt_addfun(const char *name, int(*func)()){
|
||||
|
@ -24994,8 +25270,8 @@ vec3 editor_pick(float mouse_x, float mouse_y) {
|
|||
return out;
|
||||
#else
|
||||
// unproject 2d coord as 3d coord
|
||||
vec2 dpi = ifdef(osx, window_dpi(), vec2(1,1));
|
||||
camera_t *camera = camera_get_active();
|
||||
vec2 dpi = window_dpi();
|
||||
float x = (2.0f * mouse_x) / (dpi.x * window_width()) - 1.0f;
|
||||
float y = 1.0f - (2.0f * mouse_y) / (dpi.y * window_height());
|
||||
float z = 1.0f;
|
||||
|
|
327
engine/v4k.h
327
engine/v4k.h
|
@ -123,6 +123,10 @@ extern "C" {
|
|||
#define ENABLE_LINUX_CALLSTACKS 0 ///+
|
||||
#endif
|
||||
|
||||
#ifndef ENABLE_TESTS
|
||||
#define ENABLE_TESTS 0 // ifdef(debug, 1, 0) ///+
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// if/n/def hell
|
||||
|
||||
|
@ -214,7 +218,6 @@ extern "C" {
|
|||
|
||||
// -----------------------------------------------------------------------------
|
||||
// new C keywords
|
||||
// @todo: autorun (needed?)
|
||||
|
||||
#define countof(x) (int)(sizeof (x) / sizeof 0[x])
|
||||
|
||||
|
@ -224,13 +227,13 @@ extern "C" {
|
|||
#define macro(name) concat(name, __LINE__)
|
||||
#define defer(begin,end) for(int macro(i) = ((begin), 0); !macro(i); macro(i) = ((end), 1))
|
||||
#define scope(end) defer((void)0, end)
|
||||
#define benchmark for(double macro(t) = -time_ss(); macro(t) < 0; printf("%.2fs (" FILELINE ")\n", macro(t)+=time_ss()))
|
||||
#define benchmark for(double macro(i) = 1, macro(t) = -time_ss(); macro(i); macro(t)+=time_ss(), macro(i)=0, printf("%.4fs %2.f%% (" FILELINE ")\n", macro(t), macro(t)*100/0.0166667 ))
|
||||
#define do_once static int macro(once) = 0; for(;!macro(once);macro(once)=1)
|
||||
|
||||
#if is(cl)
|
||||
#define __thread __declspec(thread)
|
||||
#elif is(tcc) && is(win32)
|
||||
#define __thread __declspec(thread) // compiles fine, but does not work apparently
|
||||
#define __thread __declspec(thread) // compiles fine apparently, but does not work
|
||||
#elif is(tcc)
|
||||
#define __thread
|
||||
#endif
|
||||
|
@ -282,6 +285,44 @@ extern "C" {
|
|||
// typedef union vec2 { float X,Y; }; vec2 a = {0,1}, b = vec2(0,1);
|
||||
#define C_CAST(type, ...) ( ifdef(c,(type),type) { __VA_ARGS__ } )
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// autorun initializers for C
|
||||
// - rlyeh, public domain
|
||||
//
|
||||
// note: based on code by Joe Lowe (public domain).
|
||||
// note: XIU for C initializers, XCU for C++ initializers, XTU for C deinitializers
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define AUTORUN \
|
||||
static void AUTORUN_U(f)(void); \
|
||||
static const int AUTORUN_J(AUTORUN_U(f),__1) = (AUTORUN_U(f)(), 1); \
|
||||
static void AUTORUN_U(f)(void)
|
||||
#elif defined _MSC_VER && !defined(__clang__) // cl, but not clang-cl
|
||||
#define AUTORUN \
|
||||
static void AUTORUN_U(f)(void); \
|
||||
static int AUTORUN_J(AUTORUN_U(f),__1) (){ AUTORUN_U(f)(); return 0; } \
|
||||
__pragma(section(".CRT$XIU", long, read)) \
|
||||
__declspec(allocate(".CRT$XIU")) \
|
||||
static int(* AUTORUN_J(AUTORUN_U(f),__2) )() = AUTORUN_J(AUTORUN_U(f),__1); \
|
||||
static void AUTORUN_U(f)(void)
|
||||
#else // gcc,tcc,clang,clang-cl...
|
||||
#define AUTORUN \
|
||||
__attribute__((constructor)) \
|
||||
static void AUTORUN_U(f)(void)
|
||||
#endif
|
||||
|
||||
// join + unique macro utils
|
||||
|
||||
#define AUTORUN_j(a, b) a##b
|
||||
#define AUTORUN_J(a, b) AUTORUN_j(a, b)
|
||||
#define AUTORUN_U(x) AUTORUN_J(x, __LINE__)
|
||||
|
||||
#if 0 // autorun demo
|
||||
void byebye(void) { puts("seen after main()"); }
|
||||
AUTORUN { puts("seen before main()"); }
|
||||
AUTORUN { puts("seen before main() too"); atexit( byebye ); }
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// build info
|
||||
|
||||
|
@ -511,7 +552,7 @@ static __thread unsigned array_n_;
|
|||
} while(0)
|
||||
|
||||
#define array_swapback_and_pop(t, i) do { /*may alter ordering*/ \
|
||||
memmove( &(t)[i], &(t)[array_count(t) - 1], sizeof(0[t])); \
|
||||
memcpy( &(t)[i], &(t)[array_count(t) - 1], sizeof(0[t])); \
|
||||
array_pop(t); \
|
||||
} while(0)
|
||||
|
||||
|
@ -1269,173 +1310,6 @@ API int pathfind_astar(int width, int height, const unsigned* map, vec2i src, ve
|
|||
// Behavior trees: decision planning and decision making.
|
||||
// Supersedes finite state-machines (FSM) and hierarchical finite state-machines (HFSM).
|
||||
// - rlyeh, public domain.
|
||||
//
|
||||
// [ref] https://outforafight.wordpress.com/2014/07/15/behaviour-behavior-trees-for-ai-dudes-part-1/
|
||||
// [ref] https://www.gameaipro.com/GameAIPro/GameAIPro_Chapter06_The_Behavior_Tree_Starter_Kit.pdf
|
||||
// [ref] https://gitlab.com/NotYetGames/DlgSystem/-/wikis/Dialogue-Manual
|
||||
// [ref] https://towardsdatascience.com/designing-ai-agents-behaviors-with-behavior-trees-b28aa1c3cf8a
|
||||
// [ref] https://docs.nvidia.com/isaac/packages/behavior_tree/doc/behavior_trees.html
|
||||
// [ref] https://docs.unrealengine.com/4.26/en-US/InteractiveExperiences/ArtificialIntelligence/BehaviorTrees/
|
||||
// [ref] gdc ChampandardDaweHernandezCerpa_BehaviorTrees.pdf @todo debugging
|
||||
// [ref] https://docs.cocos.com/cocos2d-x/manual/en/actions/
|
||||
|
||||
// The nodes in a behavior tree can be broadly categorized into three main types: control nodes, task nodes, and decorator nodes. Here is a brief description of each category:
|
||||
|
||||
// Control Nodes: Control nodes are used to control the flow of the behavior tree. They determine the order in which child nodes are executed and how the results of those nodes are combined. They are usually parent nodes.
|
||||
// Action Nodes: Action nodes are used to perform specific actions or tasks within the behavior tree. They can include actions such as moving, attacking, or interacting with objects in the game world. They are usually leaf nodes.
|
||||
// Decorator Nodes: Decorator nodes are used to modify the behavior of child nodes in some way. They can be used to repeat child nodes, invert the result of a child node, or add a cooldown period between executions. They are usually located between control and action nodes.
|
||||
|
||||
// --- VARIABLES
|
||||
|
||||
// Key Prefixes:
|
||||
// {visiblity:(l)ocal,s(q)uad,(r)ace,(f)action,(g)lobal}
|
||||
// [persistence:(t)emp,(u)serdata,(s)avegame,(c)loud] + '_' + name.
|
||||
// [persistence:(tmp),(usr)data,(sav)egame,(net)cloud] + '_' + name.
|
||||
|
||||
// Ie, l_health = 123.4, gsav_player = "john"
|
||||
|
||||
// --- ACTION NODES
|
||||
|
||||
// [ ] * Actions/Behaviors have a common structure:
|
||||
// [ ] - Entry point (function name) for a C call or Lua script.
|
||||
// [ ] * Status:
|
||||
// [ ] - Uninitialized (never run)
|
||||
// [ ] - Running (in progress)
|
||||
// [ ] - Suspended (on hold till resumed)
|
||||
// [ ] - Success (finished and succeeded)
|
||||
// [ ] - Failure (finished and failed)
|
||||
// [ ] * Optional callbacks:
|
||||
// [ ] - on_enter
|
||||
// [ ] - on_leave
|
||||
// [ ] - on_success
|
||||
// [ ] - on_failure
|
||||
// [ ] - on_suspend
|
||||
// [ ] - on_resume
|
||||
|
||||
// [x] Action Node: This node performs a single action, such as moving to a specific location or attacking a target.
|
||||
// [ ] Blackboard Node: Node that reads and writes data to a shared memory space known as a blackboard. The blackboard can be used to store information that is relevant to multiple nodes in the behavior tree.
|
||||
// [ ] SetKey(keyVar,object)
|
||||
// [ ] HasKey(keyVar)
|
||||
// [ ] CompareKeys(keyVar1, operator < <= > >= == !=, keyVar2)
|
||||
// [ ] SetTags(names=blank,cooldownTime=inf,bIsCooldownAdditive=false)
|
||||
// [ ] HasTags(names=blank,bAllRequired=true)
|
||||
// [ ] PushToStack(keyVar,itemObj): creates a new stack if one doesn’t exist, and stores it in the passed variable name, and then pushes ‘item’ object onto it.
|
||||
// [ ] PopFromStack(keyVar,itemVar): pop pops an item off the stack, and stores it in the itemVar variable, failing if the stack is already empty.
|
||||
// [ ] IsEmptyStack(keyVar): checks if the stack passed is empty and returns success if it is, and failure if its not.
|
||||
// [ ] Communication Node: This is a type of action node that allows an AI agent to communicate with other agents or entities in the game world. The node takes an input specifying the message to be communicated and the recipient(s) of the message (wildmask,l/p/f/g prefixes). The node then sends the message to the designated recipient(s) and returns success when the communication is completed. This node can be useful for implementing behaviors that require the AI agent to coordinate with other agents or to convey information to the player. It could use a radius argument to specify the maximum allowed distance for the recipients.
|
||||
// [ ] Condition Node: A leaf node that checks a specific condition, such as the distance to an object, the presence of an enemy, or the status of a health bar.
|
||||
// [ ] Distance Condition Node: This is a type of condition node that evaluates whether an AI agent is within a specified distance of a target object or location. The node takes two inputs: the current position of the AI agent and the position of the target object or location. If the distance between the two is within a specified range, the node returns success. If the distance is outside of the specified range, the node returns failure. This node can be useful for implementing behaviors that require the AI agent to maintain a certain distance from a target, such as following or avoiding an object. Could use a flag to disambiguate between linear distance and path distance.
|
||||
// [ ] Query Node: This node checks a condition and returns success or failure based on the result. For example, a query node could check whether an enemy is within range or whether a door is locked.
|
||||
// [ ] Query Node: A type of decorator node that retrieves information from a database or external system, such as a web service or file system. The Query node can be used to retrieve data that is not available within the game engine, such as weather or traffic conditions.
|
||||
// [ ] A condition is made of:
|
||||
// [ ] - Optional [!] negate
|
||||
// [ ] - Mandatory Value1(Int/Flt/Bool/VarName/FuncName)
|
||||
// [ ] - Optional operator [< <= > >= == !=] and Value2(Int/Flt/Bool/VarName/FuncName)
|
||||
// [ ] AllConditions(...) : SUCCESS if ( empty array || all conditions met)
|
||||
// [ ] AnyConditions(...) : SUCCESS if (!empty array && one conditions met)
|
||||
|
||||
// --- DECORATOR NODES
|
||||
|
||||
// [ ] Cooldown Node: Decorator node that adds a cooldown period between the execution of a child node, preventing it from being executed again until the cooldown period has elapsed.
|
||||
// [x] Counter Node: Decorator node that limits the number of times that a child node can execute. For example, if the child node has executed a certain number of times, the Counter node will return a failure.
|
||||
// [x] Once Node: Decorator node that triggers a specified action when a condition is met for the first time. This can be useful for triggering a one-time event, such as when an AI agent discovers a hidden item or reaches a new area of the game world.
|
||||
// [x] Inverter Node: Decorator node that inverts the result of a child node, returning success if the child node fails and failure if the child node succeeds. This can be useful for negating the outcome of a particular behavior, such as avoiding a certain area of the game world.
|
||||
// [x] Repeater Node: Decorator node that repeats the execution of a child node a specified number of times or indefinitely.
|
||||
// [x] Repeat(times=inf): Runs child node given times. These are often used at the very base of the tree, to make the tree to run continuously.
|
||||
// [ ] RepeatIf(strong/weak condition): Runs child node as long as the conditions are met.
|
||||
// [ ] RepeatIfOk(times=inf): Runs child node if it succeedes, max given times.
|
||||
// [ ] RepeatIfFail(times=inf): Runs child node if it fails, max given times.
|
||||
// [ ] Branch Node: 2 children [0] for true, [1] for false
|
||||
// [ ] Resource Node: Decorator node that manages a shared resource, such as a limited supply of ammunition or a limited amount of processing power. The Resource node The node can be used to decide when to use or conserve the resource. For example, if the AI agent is low on ammunition, the node may decide to switch to a melee weapon or to retreat to a safer location. This node can be useful for implementing behaviors that require the AI agent to make strategic decisions about resource use.
|
||||
// [x] Result Node: Decorator node that tracks the result of a child action node and returns either success or failure depending on the outcome. This can be useful for determining whether an action was successful or not, and then adjusting the AI agent's behavior accordingly.
|
||||
// [x] Succeeder Node: Decorator node that always returns success, regardless of the result of its child node. This can be useful for ensuring that certain behaviors are always executed, regardless of the overall success or failure of the behavior tree.
|
||||
// [x] Success(): FAILURE becomes SUCCESS (TRUE).
|
||||
// [x] Failure(): SUCCESS becomes FAILURE (FALSE).
|
||||
// [ ] Throttle Node: Decorator node that limits the rate at which a child node can execute. For example, a Throttle node might ensure that an AI agent can only fire its weapon, or using a special ability, only a certain number of times per second.
|
||||
// [x] Delay Node: Decorator node that adds a delay to the execution of a child node. The delay might be configured to sleep before the execution, after the execution, or both.
|
||||
// [x] Defer Delay(duration_ss=1): Runs the child node, then sleeps for given seconds.
|
||||
// [ ] Ease(time,name): Clamps child time to [0,1] range, and applies easing function on it.
|
||||
// [ ] Dilate(Mul=1,Add=0): Dilates child time
|
||||
|
||||
// --- CONTROL NODES
|
||||
|
||||
// [x] Root Node: The topmost node in a behavior tree that represents the start of the decision-making process. Returns success if any of its child nodes suceedes.
|
||||
// [x] Root-Failure Node: Control node that only returns failure if all of its child nodes fail. This can be useful for ensuring that a behavior tree is not prematurely terminated if one or more child nodes fail.
|
||||
|
||||
// [ ] Event(name): When name event is raised, it suspends current tree and calls child. "Incoming projectile" -> Evade. Stimulus types: may be disabled by event, or autodisabled.
|
||||
// [ ] Raise(name): Triggers event name.
|
||||
|
||||
// [ ] Checkpoint Node: Control node that saves a state in memory and then continues the tree execution from that point the next time the tree is executed. It can be useful in situations where the behavior tree needs to be interrupted and resumed later.
|
||||
// [ ] Decision Making Node: Control node that implements a decision-making process for the AI agent. The node takes input specifying the available options for the AI agent and the criteria for evaluating each option. The node then evaluates each option based on the specified criteria and selects the best option. This node can be useful for implementing behaviors that require the AI agent to make strategic decisions, such as choosing a target or selecting a path through the game world.
|
||||
// [ ] Could be extended with GOAP if dynamically inserting the scores on each update then calling a Probability Selector Node (0.2,0.3,0.5)
|
||||
// [ ] https://cdn.cloudflare.steamstatic.com/apps/valve/2012/GDC2012_Ruskin_Elan_DynamicDialog.pdf
|
||||
// [ ] Evaluate Node / Recheck():
|
||||
// [ ] Actively rechecks all existing sub-conditions on a regular basis after having made decisions about them.
|
||||
// [ ] Use this feature to dynamically check for risks or opportunities in selected parts of the tree.
|
||||
// [ ] For example, interrupting a patrol with a search behavior if a disturbance is reported.
|
||||
// [ ] Interrupt Node: Control node that interrupts the execution of a lower-priority node when a higher-priority node needs to be executed. It can be useful for handling urgent tasks or emergency situations.
|
||||
// [ ] Monitor Node: Control node that continuously monitors a condition and triggers a specified action when the condition is met. For example, a Monitor node might monitor the distance between an AI agent and a target and trigger a "retreat" behavior when the distance falls below a certain threshold.
|
||||
// [ ] Observer Node: Control node that monitors the state of the game world and triggers a specified action when certain conditions are met. For example, an Observer node might trigger a "hide" behavior when an enemy is spotted.
|
||||
// [ ] Parallel Node: Control node that executes multiple child nodes simultaneously. The Parallel node continues to execute even if some of its child nodes fail.
|
||||
// [ ] Parallel All Node(required_successes=100%): Control node that executes multiple child nodes simultaneously. Returns false when first child fails, and aborts any other running tasks. Returns true if all its children succeed.
|
||||
// [ ] Parallel One Node(required_successes=1): Control node that executes multiple child nodes simultaneously. Returns true when first child suceedes, and aborts any other running tasks. Returns false if all its children fail.
|
||||
// [ ] Subsumption Node: Control node that allows multiple behaviors to be executed simultaneously, with higher-priority behaviors taking precedence over lower-priority ones. This can be useful for implementing complex, multi-level behaviors in autonomous systems.
|
||||
// [ ] Semaphore Node: Control node that blocks the execution of its child nodes until a certain condition is met. For example, a Semaphore node might block the execution of a behavior until a certain object is in range or a certain event occurs.
|
||||
// [ ] Semaphore Wait Node: Control node that blocks the execution of its child nodes until a resource becomes available. This can be useful for controlling access to shared resources such as a pathfinding system or a communication channel.
|
||||
// [ ] WaitTags(tags=blank,timeout_ss=inf): Stops execution of child node until the cooldown tag(s) do expire. May return earlier if timed out.
|
||||
// [ ] WaitEvent(name=blank,timeout_ss=inf): Stops execution of child node until event is raised. May return earlier if timed out.
|
||||
// [x] Sequence Node(reversed,iterator(From,To)): Control node that executes a series of child nodes in order. If any of the child nodes fail, the Sequence node stops executing and returns a failure.
|
||||
// [ ] Dynamic Sequence Node: Control node that dynamically changes the order in which its child nodes are executed based on certain conditions. For example, a Dynamic Sequence node might give higher priority to a child node that has recently failed or that is more likely to succeed.
|
||||
// [ ] Reverse(): iterates children in reversed order (Iterator:(-1,0) equivalent)
|
||||
// [ ] Iterator(from,to): allows negative indexing. increments if (abs from < abs to), decrements otherwise
|
||||
// [x] Selector Node: Control node that selects a child node to execute based on a predefined priority or set of conditions. The Selector node stops executing child nodes as soon as one of them succeeds.
|
||||
// [ ] Priority Selector Node: Control node that executes its child nodes in order of priority. If the first child node fails, it moves on to the next child node in the list until a successful node is found. This can be useful for implementing behaviors that require a specific order of execution, such as a patrol route or a search pattern.
|
||||
// [ ] Probability Selector Node: Control node that selects a child node to execute based on a probability distribution. For example, if there are three child nodes with probabilities of 0.2, 0.3, and 0.5, the Probability Selector node will execute the third child node 50% of the time.
|
||||
// [ ] Dynamic Selector Node: Control node that dynamically changes the order in which its child nodes are executed based on certain conditions. For example, a Dynamic Selector node might give higher priority to a child node that has recently succeeded or that is more likely to succeed.
|
||||
// [ ] Dynamic Selector Node: Control node that dynamically changes the order in which child nodes are executed based on certain conditions. For example, it may prioritize certain nodes when health is low, and other nodes when resources are scarce.
|
||||
// [ ] Dynamic Priority Node: Control node that dynamically changes the priority of its child nodes based on certain conditions. For example, if a child node is more likely to result in success, the Dynamic Priority node will give it a higher priority.
|
||||
// [ ] Weighted / Cover Selection Node: This is a type of selector node that evaluates the available cover options in the game world and selects the best cover position for the AI agent. The node takes input specifying the current position of the AI agent and the location of potential cover positions. The node then evaluates the cover positions based on criteria such as distance to enemy positions, line of sight, and available cover points, and selects the best option. This node can be useful for implementing behaviors that require the AI agent to take cover and avoid enemy fire.
|
||||
// [ ] Random Selector Node: Control node that selects a child node to execute at random. This can be useful for introducing variety and unpredictability into the behavior of an AI agent.
|
||||
// [ ] Random Weight / Stochastic Node(0.2,0.3,0.5): Control node that introduces randomness into the decision-making process of an AI agent. For example, a Stochastic node might randomly select a child node to execute, with the probability of each child node being proportional to its likelihood of success.
|
||||
// [ ] Break(bool): breaks parent sequence or selector. may return SUCCESS or FAILURE.
|
||||
// [ ] Hybrid Node: Control node that combines the functionality of multiple control nodes, such as a sequence node and a selector node. It can be useful for creating complex behavior patterns that require multiple control structures. The Hybrid Node has two modes of operation: strict mode and flexible mode. In strict mode, the child nodes are executed in a fixed order, similar to a Sequence Node. If any child node fails, the entire Hybrid Node fails. In flexible mode, the child nodes can be executed in any order, similar to a Selector Node. If any child node succeeds, the entire Hybrid Node succeeds. The Hybrid Node is often used when you need to create complex behavior patterns that require multiple control structures. For example, you might use a Hybrid Node to implement a search behavior where the AI agent first moves in a fixed direction for a certain distance (strict mode), but then switches to a more exploratory mode where it randomly explores nearby areas (flexible mode).
|
||||
// [ ] Subtree Node: Control node that calls another behavior tree as a subroutine. This is useful for breaking down complex behaviors into smaller, more manageable parts.
|
||||
// [ ] Call(name): Calls to the child node with the matching name within behavior tree. Returns FAILURE if name is invalid or if invoked behavior returns FAILURE.
|
||||
// [ ] Return(boolean): Exits current Call.
|
||||
// [ ] AttachTree(Name,bDetachAfterUse=true): Attaches subtree to main tree. When a NPC founds the actor, it attaches the behavior to the tree. Ie, Level specific content: Patrols, Initial setups, Story driven events, etc. Ie, DLCs: Behaviors are added to actors in the DLC level (enticers).
|
||||
// [ ] DetachTree(Name)
|
||||
// [ ] Switch Node: Control node that evaluates a condition and then selects one of several possible child nodes to execute based on the result of the condition.
|
||||
// [ ] Switch(name): Jumps to the child node with the matching name within behavior tree. If name is invalid will defer to Fallback child node.
|
||||
// [ ] Timeout Node: Control node that aborts the execution of its child node after a certain amount of time has elapsed. This can be useful for preventing an AI agent from getting stuck in a loop or waiting indefinitely for a particular event to occur.
|
||||
// [ ] TimeLimit(timeout_ss=inf): Give the child any amount of time to finish before it gets canceled. The timer is reset every time the node gains focus.
|
||||
// [ ] Timer Node: Control node which invokes its child node every XX seconds. The timer can repeat the action any given number of times, or indefinitely.
|
||||
|
||||
// ## Proposal -----------------------------------------------------------------------------
|
||||
|
||||
// BehaviorTrees as case-insensitive INI files. Then,
|
||||
// - INI -> C INTERPRETER, OR
|
||||
// - INI -> LUA TRANSPILER -> LUA BYTECODE -> LUA VM.
|
||||
|
||||
// ```ini
|
||||
// [bt]
|
||||
// recheck
|
||||
// sequence ;; Approach the player if seen!
|
||||
// conditions=IsPlayerVisible,
|
||||
// action=MoveTowardsPlayer
|
||||
// sequence ;; Attack the player if seen!
|
||||
// conditions=IsPlayerInRange,
|
||||
// repeat=3
|
||||
// action=FireAtPlayer
|
||||
// sequence ;; Search near last known position
|
||||
// conditions=HaveWeGotASuspectedLocation,
|
||||
// action=MoveToPlayersLastKnownPosition
|
||||
// action=LookAround
|
||||
// sequence ;; Randomly scanning nearby
|
||||
// action=MoveToRandomPosition
|
||||
// action=LookAround
|
||||
// event=IncomingProjectile
|
||||
// action=Evade
|
||||
// ```
|
||||
|
||||
typedef int (*bt_func)();
|
||||
|
||||
|
@ -2630,44 +2504,6 @@ API int semvercmp( int v1, int v2 );
|
|||
#define SEMVERCMP(v1,v2) (((v1) & 0110) - ((v2) & 0110))
|
||||
#define SEMVERFMT "%03o"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// autorun initializers for C
|
||||
// - rlyeh, public domain
|
||||
//
|
||||
// note: based on code by Joe Lowe (public domain).
|
||||
// note: XIU for C initializers, XCU for C++ initializers, XTU for C deinitializers
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define AUTORUN \
|
||||
static void AUTORUN_U(f)(void); \
|
||||
static const int AUTORUN_J(AUTORUN_U(f),__1) = (AUTORUN_U(f)(), 1); \
|
||||
static void AUTORUN_U(f)(void)
|
||||
#elif _MSC_VER
|
||||
#define AUTORUN \
|
||||
static void AUTORUN_U(f)(void); \
|
||||
static int AUTORUN_J(AUTORUN_U(f),__1) (){ AUTORUN_U(f)(); return 0; } \
|
||||
__pragma(section(".CRT$XIU", long, read)) \
|
||||
__declspec(allocate(".CRT$XIU")) \
|
||||
static int(* AUTORUN_J(AUTORUN_U(f),__2) )() = AUTORUN_J(AUTORUN_U(f),__1); \
|
||||
static void AUTORUN_U(f)(void)
|
||||
#else
|
||||
#define AUTORUN \
|
||||
__attribute__((constructor)) \
|
||||
static void AUTORUN_U(f)(void)
|
||||
#endif
|
||||
|
||||
// join + unique macro utils
|
||||
|
||||
#define AUTORUN_j(a, b) a##b
|
||||
#define AUTORUN_J(a, b) AUTORUN_j(a, b)
|
||||
#define AUTORUN_U(x) AUTORUN_J(x, __LINE__)
|
||||
|
||||
#if 0 // autorun demo
|
||||
void byebye(void) { puts("seen after main()"); }
|
||||
AUTORUN { puts("seen before main()"); }
|
||||
AUTORUN { puts("seen before main() too"); atexit( byebye ); }
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// storage types. refer to vec2i/3i, vec2/3/4 if you plan to do math operations
|
||||
|
||||
|
@ -2744,6 +2580,66 @@ extern API int profiler_enabled; ///-
|
|||
#endif
|
||||
#line 0
|
||||
|
||||
#line 1 "v4k_reflect.h"
|
||||
// C reflection: enums, functions, structs, members and anotations.
|
||||
// - rlyeh, public domain
|
||||
//
|
||||
// @todo: nested structs? pointers in members?
|
||||
// @todo: declare TYPEDEF(vec3, float[3]), TYPEDEF(mat4, vec4[4]/*float[16]*/)
|
||||
|
||||
#define ifdef_objapi(T,...) __VA_ARGS__
|
||||
|
||||
typedef struct reflected_t {
|
||||
unsigned id, objtype;
|
||||
unsigned sz;
|
||||
const char *name;
|
||||
const char *info;
|
||||
void *addr;
|
||||
unsigned parent;
|
||||
const char *type;
|
||||
} reflected_t;
|
||||
|
||||
// inscribe api
|
||||
|
||||
#define ENUM(V, value_annotations) \
|
||||
enum_inscribe(#V,intern(#V),V, value_annotations)
|
||||
|
||||
#define FUNCTION(F, function_annotations) \
|
||||
function_inscribe(#F,intern(#F),(void*)F, function_annotations)
|
||||
|
||||
#define STRUCT(T, type, member, member_annotations) \
|
||||
struct_inscribe(#T,intern(#T),sizeof(T),ifdef(objapi,OBJTYPE_##T,0),NULL), \
|
||||
type_inscribe(#type,intern(#type),sizeof(((T){0}).member),member_annotations), \
|
||||
member_inscribe(intern(#T), #member,intern(#member),(uintptr_t)&((T*)0)->member, member_annotations, #type )
|
||||
|
||||
// find api
|
||||
|
||||
API unsigned enum_find(const char *E);
|
||||
API void * function_find(const char *F);
|
||||
|
||||
API reflected_t member_find(const char *T, const char *M); /// find specific member
|
||||
API void * member_findptr(void *obj, const char *T, const char *M);
|
||||
API array(reflected_t) members_find(const char *T);
|
||||
|
||||
// iterate members in a struct
|
||||
|
||||
#define each_member(T,R) \
|
||||
(array(reflected_t)*found_ = map_find(members, intern(T)); found_; found_ = 0) \
|
||||
for(int it_ = 0, end_ = array_count(*found_); it_ != end_; ++it_ ) \
|
||||
for(reflected_t *R = (*found_)+it_; R; R = 0 )
|
||||
|
||||
// private api, still exposed
|
||||
|
||||
API void type_inscribe(const char *TY,unsigned TYid,unsigned TYsz,const char *infos);
|
||||
API void enum_inscribe(const char *E,unsigned Eid,unsigned Eval,const char *infos);
|
||||
API void struct_inscribe(const char *T,unsigned Tid,unsigned Tsz,unsigned OBJTYPEid, const char *infos);
|
||||
API void member_inscribe(unsigned Tid, const char *M,unsigned Mid,unsigned Msz, const char *infos, const char *type);
|
||||
API void function_inscribe(const char *F,unsigned Fid,void *func,const char *infos);
|
||||
|
||||
API void reflected_printf(reflected_t *r);
|
||||
API void reflected_printf_all();
|
||||
#line 0
|
||||
|
||||
#line 1 "v4k_render.h"
|
||||
// -----------------------------------------------------------------------------
|
||||
// naive rendering framework
|
||||
|
@ -3872,7 +3768,15 @@ API int (PRINTF)(const char *text, const char *stack, const char *file, int line
|
|||
|
||||
#define test(expr) test(__FILE__,__LINE__,#expr,!!(expr))
|
||||
API int (test)(const char *file, int line, const char *expr, bool result);
|
||||
// AUTORUN { test(1<2); }
|
||||
|
||||
#if ENABLE_TESTS
|
||||
#define AUTOTEST AUTORUN
|
||||
#else
|
||||
#define AUTOTEST static void concat(concat(concat(disabled_test_, __LINE__), _), __COUNTER__)()
|
||||
#endif
|
||||
|
||||
// AUTOTEST { test(1<2); }
|
||||
|
||||
#line 0
|
||||
|
||||
#line 1 "v4k_ui.h"
|
||||
|
@ -4056,7 +3960,6 @@ API char* window_stats();
|
|||
API uint64_t window_frame();
|
||||
API int window_width();
|
||||
API int window_height();
|
||||
API vec2 window_dpi();
|
||||
API double window_time();
|
||||
API double window_delta();
|
||||
|
||||
|
@ -4090,6 +3993,8 @@ API void window_fps_unlock();
|
|||
API void window_screenshot(const char* outfile_png); // , bool record_cursor
|
||||
API int window_record(const char *outfile_mp4); // , bool record_cursor
|
||||
|
||||
API vec2 window_dpi();
|
||||
|
||||
enum CURSOR_SHAPES {
|
||||
CURSOR_NONE,
|
||||
CURSOR_HW_ARROW, // default
|
||||
|
|
Loading…
Reference in New Issue