Vitals
Example apps / vitals / v0.1.0
HUD widgetIn the download
A HUD widget with V's health, level and money, read through the generic game API from a web page.
Simulated game
Mod log
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Vitals</title>
<script src="rednode://ui/rednode-ui.js"></script>
<script type="importmap">{ "imports": { "rednode": "rednode://sdk/index.js" } }</script>
<style>
/* A HUD widget: V's health, level and money, read through the generic game API.
The Game API mod reads the same values from Node; both should agree. */
html, body { margin: 0; width: 300px; height: 64px; overflow: hidden; background: transparent; }
.strip { position: absolute; inset: 6px 0 auto 0; display: flex; justify-content: center; gap: 16px; padding: 6px 14px;
background: linear-gradient(90deg, transparent, var(--rn-bg) 12%, var(--rn-bg) 88%, transparent);
font: 600 15px var(--rn-mono); letter-spacing: .05em; color: var(--rn-fg); white-space: nowrap; }
.k { color: var(--rn-accent); font-size: 11px; margin-right: 6px; letter-spacing: .14em; }
</style>
</head>
<body theme="hud">
<div class="strip">
<span><span class="k">HP</span><span id="hp">-</span></span>
<span><span class="k">LVL</span><span id="lvl">-</span></span>
<span><span class="k">€$</span><span id="money">-</span></span>
</div>
<script type="module">
import { api, game } from 'rednode';
const set = (id, v) => (document.getElementById(id).textContent = v);
async function refresh() {
try {
const player = await api.GetPlayer(game.instance);
if (!player) return;
const statsId = await api.$call('Cast;EntityID;StatsObjectID', await player.GetEntityID());
const pools = await api.GameInstance.GetStatPoolsSystem(game.instance);
const stats = await api.GameInstance.GetStatsSystem(game.instance);
const shop = await api.GameInstance.GetTransactionSystem(game.instance);
set('hp', Math.round(await pools.GetStatPoolValue(statsId, 'Health', false)));
set('lvl', await stats.GetStatValue(statsId, 'Level'));
set('money', await shop.GetItemQuantity(player, await api.MarketSystem.Money()));
} catch {
// no player yet: the plugin hides this widget at the main menu anyway
}
}
setInterval(refresh, 1000);
refresh();
</script>
</body>
</html>
import { api, game } from 'rednode';
const set = (id, v) => (document.getElementById(id).textContent = v);
async function refresh() {
try {
const player = await api.GetPlayer(game.instance);
if (!player) return;
const statsId = await api.$call('Cast;EntityID;StatsObjectID', await player.GetEntityID());
const pools = await api.GameInstance.GetStatPoolsSystem(game.instance);
const stats = await api.GameInstance.GetStatsSystem(game.instance);
const shop = await api.GameInstance.GetTransactionSystem(game.instance);
set('hp', Math.round(await pools.GetStatPoolValue(statsId, 'Health', false)));
set('lvl', await stats.GetStatValue(statsId, 'Level'));
set('money', await shop.GetItemQuantity(player, await api.MarketSystem.Money()));
} catch {
// no player yet: the plugin hides this widget at the main menu anyway
}
}
setInterval(refresh, 1000);
refresh();
REDnode runs JavaScript. The rednode package also has TypeScript types, so an editor checks these calls; compile TypeScript to .mjs first.
{
"id": "vitals",
"name": "Vitals",
"version": "0.1.0",
"author": "Edgemodders",
"description": "A HUD widget with V's health, level and money, read through the generic game API from a web page. The Game API mod reads the same from Node.",
"entry": "index.html",
"kind": "hud",
"hideWith": ["menu", "photo-mode", "scene", "main-menu"],
"width": 300,
"height": 64,
"anchor": "top",
"x": 0,
"y": 80,
"open": true
}
The manifest names the package and says how REDnode runs it. Its fields are in Overlay apps.
How it works
The same calls as the Game API mod, from a page instead of Node: the rednode package works in both.
What it uses
kind: "hud" api.GetPlayer api.GameInstance api.$call
Try this
- Press Hurt V below and watch HP refill.
Use it
Copy the files into red4ext/plugins/REDnode/apps/vitals/ in the game folder, or start your own package from them: change id and name in the manifest first. The whole folder is on GitHub.