Small static helper classes you can call from anywhere. They expose only static
functions (no autoload needed), except Generate which loads a project asset.
__— Underscore — safe object access, colors, BBCodeWait— timers — await delays without boilerplateTimeTools— dates — date formatting and countdownsBundle— bundle settings — read store/bundle metadataGenerate— random ids/names — uids and player names
Inspired by Underscore.js. Safe access to nested
data without crashing on null.
Reads a dotted path from a Dictionary or Object, returning null if any
segment is missing:
var record = __.Get('newRecord', body) # body.newRecord
var x = __.Get('player.position.x', state) # nestedSame, with a fallback when the result is null:
var delay = __.GetOr(0, 'delay', options)Writes a (possibly nested) dotted path into a Dictionary or Object:
__.Set(100, 'score', state)
__.Set(true, 'flags.muted', state)Builds a Color from a hex string (#rgb, #rgba, #rrggbb, #rrggbbaa):
var c = __.useColor('#A1553E')Converts Godot BBCode tags to terminal ANSI escape codes. This is what powers
the colored output of G.log — you rarely call it directly.
Timer helpers to await delays without wiring a Timer by hand.
Creates a one-shot Timer as a child of parent and returns it, so you can
await its timeout:
await Wait.forSomeTime(self, 2).timeoutA delay of 0 returns {timeout = true} so awaiting still resolves
immediately.
A debounced timer: re-calling it restarts the same timer instead of stacking
new ones (useful for "do X once the user stops doing Y"). The object must own a
params Dictionary where the timer is cached:
var params = {} # the object must expose this
func onTyping():
Wait.withTimer(0.5, self, func(): G.log('stopped typing'))Date/time helpers built on UTC. Datetime arguments are Godot datetime
dictionaries (Time.get_datetime_dict_from_unix_time(...)).
TimeTools.dateTimeToYYYYMMDDNumber(datetime)→ e.g.20240820TimeTools.dateTimeToYYYYMMNumber(datetime)→ e.g.202408TimeTools.dateTimeToReadableDate(datetime)→ e.g.2024-03-13 10:29TimeTools.getDeviceTodayNumUTC()→ today as ayyyymmddnumberTimeTools.getTimeRemainingForToday()→"HH:MM:SS"until midnightTimeTools.getTimeRemainingForSeason()→{nbDays, timeBeforeMidnight}until the 1st of next monthTimeTools.getTimeRemainingForThisWeek()→{nbDays, timeBeforeMidnight}until next MondayTimeTools.nbDaysInMonth(month, year)→ days in a month (leap-year aware)
var today = TimeTools.getDeviceTodayNumUTC() # 20240820
var countdown = TimeTools.getTimeRemainingForToday() # "08:14:52"Reads bundle/store metadata from project.godot [bundle] settings and from
fox.config.json (bundles section). Useful for "rate this app" / store links.
Bundle.getTitle()→bundle/titleBundle.getSubtitle()→bundle/subtitleBundle.getPlatform()→bundle/platformBundle.getAppId()→ iOS app id of the current bundleBundle.getStoreUrl()→ store URL of the current bundle for the current platform
if Bundle.getPlatform() == 'iOS':
OS.shell_open('itms-apps://itunes.apple.com/app/' + Bundle.getAppId())
else:
OS.shell_open(Bundle.getStoreUrl())Generates random ids and player names. Names are drawn from
res://assets/name-elements.json (a {adjectives: [...], names: [...]}
file you provide), so add it as an Autoload (e.g. Generate) since it loads
a project asset:
[autoload]
Generate="*res://fox/libs/generate.gd"Generate.uid(prefix)→ a unique id, e.g.player-1718000000-12345-678901(pass''ornullfor no prefix)Generate.name()→ a randomAdjectiveName(falls back to'Player'if the asset is missing)
var id = Generate.uid('player')
var nick = Generate.name() # "FieryFox"