rules
Banana 2077's one shared module: palette, the Peel Kabuki street layout every slice builds to, tuning for the player, baton and enemies, the controls list, the live game state (`R.state`, kept across hot reloads), the event bus (`R.on` / `R.emit`) and the rule functions that chan…
game.rules
Banana 2077's one shared module: palette, the Peel Kabuki street layout every slice builds to, tuning for the player, baton and enemies, the controls list, the live game state (R.state, kept across hot reloads), the event bus (R.on / R.emit) and the rule functions that change state (damagePlayer, healPlayer, addEddies, setSunglasses, registerTarget, hit, reportKill, setPhase, startRun). Required by every slice; owned by the lead. See /zero/source/design/spec.md.
Interface
What this asset declares: the schema it conforms to, what it exposes, and the rendered structured payload.
conforms to
zero/source-extract/v2strict
now( ) → number
fresh( ) → void
on(event: string, fn: Listener) → void
Event bus events: "phaseChanged" {from,to} "reset" {} "swing" {combo,heavy} "hit" {target,kind,damage,heavy,pos,dir} "enemyKilled" {kind,pos,eddies,boss} "playerDamaged" {amount,health,fromPos} "playerHealed" {amount,health} "eddies" {amount,total} "sunglassesToggled" {on} "bossCue" {} "dodge" {} "footstep" {sprint}
| arg | type | description |
|---|---|---|
| event | string | |
| fn | Listener |
clearListeners( ) → void
Drops every subscription (the scene entrypoint calls this on unload).
emit(event: string, payload: any?) → void
| arg | type | description |
|---|---|---|
| event | string | |
| payload | any? |
setPhase(phase: string) → void
Phases
| arg | type | description |
|---|---|---|
| phase | string |
isPlaying( ) → boolean
reset( ) → void
Reset the run (title -> playing, or retry after a loss). Every slice listens to "reset" and puts its own pieces back.
startRun( ) → void
Start (or restart) a run: reset everything, then switch to playing.
setPlayerPose(x: number, y: number, z: number, yaw: number?) → void
Player
| arg | type | description |
|---|---|---|
| x | number | |
| y | number | |
| z | number | |
| yaw | number? |
setInvulnerable(seconds: number) → void
| arg | type | description |
|---|---|---|
| seconds | number |
damagePlayer(amount: number, fromPos: any?) → boolean
Enemies call this to hurt the player. Returns true when the damage landed.
| arg | type | description |
|---|---|---|
| amount | number | |
| fromPos | any? |
healPlayer(amount: number) → void
| arg | type | description |
|---|---|---|
| amount | number |
addEddies(amount: number) → void
| arg | type | description |
|---|---|---|
| amount | number |
setSunglasses(on: boolean) → void
| arg | type | description |
|---|---|---|
| on | boolean |
registerTarget(target: any) → void
Hit targets. The enemies slice registers every live enemy; the player's baton queries them and calls M.hit. target = { id = string, kind = string, position = () -> {x,y,z}, radius = number, takeHit = (damage, dir {x,z}, heavy) -> () }
| arg | type | description |
|---|---|---|
| target | any |
unregisterTarget(id: string) → void
| arg | type | description |
|---|---|---|
| id | string |
targets( ) → void
Every live target, keyed by id. This is the live table: iterate it with `for _, t in R.targets() do` (no copy is made). A target may unregister while you iterate (a kill), which Luau allows; do not add targets mid-loop. Optional fields an enemy target carries: label, level(), hp(), maxHp().
hit(target: any, damage: number, dir: any, heavy: boolean, crit: boolean?) → void
Called by the player slice when a swing connects. `crit` marks a critical roll from R.rollDamage; `dir.power` scales knockback.
| arg | type | description |
|---|---|---|
| target | any | |
| damage | number | |
| dir | any | |
| heavy | boolean | |
| crit | boolean? |
reportKill(kind: string, pos: any, info: any?) → void
Called by the enemies slice when an enemy's hp reaches 0. info (optional) = { level = number?, encounter = string?, ambient = boolean? } Encounter and ambient kills give xp and eddies but do not count toward the street's 8 thugs or the boss.
| arg | type | description |
|---|---|---|
| kind | string | |
| pos | any | |
| info | any? |
objectiveText( ) → string
xpToNext(level: number) → number
XP needed to go from `level` to `level + 1`.
| arg | type | description |
|---|---|---|
| level | number |
defineEnemy(kind: string, spec: any) → void
Slices may add enemy kinds (other gangs) with the same fields as M.enemies.tomato (+ xp, label, juice, height).
| arg | type | description |
|---|---|---|
| kind | string | |
| spec | any |
defineItem(id: string, def: any) → void
| arg | type | description |
|---|---|---|
| id | string | |
| def | any |
item(id: string, def: any) → void
| arg | type | description |
|---|---|---|
| id | string | |
| def | any |
newSkills( ) → any
newProfile( ) → any
migrate(p: any) → any
Fill fields a saved profile from an older version lacks.
| arg | type | description |
|---|---|---|
| p | any |
profile( ) → any
tierOf(def: any) → number
An item's tier 1..5 (Common..Legendary). Catalog items carry `rarity`; items a slice defines without one are tiered by price.
| arg | type | description |
|---|---|---|
| def | any |
defineGear(gid: string, spec: any) → any
Gear instances (dropped or upgraded pieces) live in profile.gear and are defined into M.items under their own "gear_N" ids.
| arg | type | description |
|---|---|---|
| gid | string | |
| spec | any |
defineAllGear(p: any) → void
| arg | type | description |
|---|---|---|
| p | any |
store( ) → any
save( ) → void
Write the profile now. Slices never need to call this: every change below schedules a save on its own.
dirty( ) → void
hasSave( ) → boolean
refreshStats( ) → void
Recompute derived stats (max health tracks body, level and gear).
applyProfile(p: any, isNew: boolean) → void
| arg | type | description |
|---|---|---|
| p | any | |
| isNew | boolean |
loadGame( ) → boolean
Load the saved profile (or start a new one when there is none). The scene entrypoint calls this on load.
newGame( ) → void
Wipe the profile and the save: a brand-new V.
notify(text: string, kind: string?, color: string?) → void
Notifications: any slice may post one; the HUD shows them as toasts. kind: "info" | "xp" | "level" | "item" | "quest" | "romance" | "eddies" | "warn" | "hack"
| arg | type | description |
|---|---|---|
| text | string | |
| kind | string? | |
| color | string? |
johnnySays(text: string, seconds: number?) → void
A line from Johnny Silverpeel, the engram in V's head (story slice draws it).
| arg | type | description |
|---|---|---|
| text | string | |
| seconds | number? |
level( ) → number return profile
Progression
xp( ) → void
attribute(name: string) → number return profile
| arg | type | description |
|---|---|---|
| name | string |
attributePoints( ) → number return profile
check(attr: string, n: number) → boolean
An attribute check for dialogue and doors: true when V has at least n.
| arg | type | description |
|---|---|---|
| attr | string | |
| n | number |
awardXp(amount: number, reason: string?) → void
| arg | type | description |
|---|---|---|
| amount | number | |
| reason | string? |
spendAttribute(name: string) → boolean
| arg | type | description |
|---|---|---|
| name | string |
maxHealth(a: any, L: number) → void
| arg | type | description |
|---|---|---|
| a | any | |
| L | number |
damageMult(a: any, L: number) → void
| arg | type | description |
|---|---|---|
| a | any | |
| L | number |
critChance(a: any, _L: number) → void
| arg | type | description |
|---|---|---|
| a | any | |
| _L | number |
critMult(a: any, _L: number) → void
| arg | type | description |
|---|---|---|
| a | any | |
| _L | number |
armor(a: any, _L: number) → void
| arg | type | description |
|---|---|---|
| a | any | |
| _L | number |
maxRam(a: any, _L: number) → void
| arg | type | description |
|---|---|---|
| a | any | |
| _L | number |
ramRegen(a: any, _L: number) → void
| arg | type | description |
|---|---|---|
| a | any | |
| _L | number |
quickhackMult(a: any, L: number) → void
| arg | type | description |
|---|---|---|
| a | any | |
| L | number |
breachTime(a: any, _L: number) → void
| arg | type | description |
|---|---|---|
| a | any | |
| _L | number |
priceMult(a: any, _L: number) → void
| arg | type | description |
|---|---|---|
| a | any | |
| _L | number |
dodgeCooldownMult(a: any, _L: number) → void
| arg | type | description |
|---|---|---|
| a | any | |
| _L | number |
perkSum(p: any, name: string) → number
| arg | type | description |
|---|---|---|
| p | any | |
| name | string |
stat(name: string) → number
A derived stat: attribute curve + perks × rank + skill levels + equipped gear (cyberware boosted by the cyberwareMult perk).
| arg | type | description |
|---|---|---|
| name | string |
rollDamage(base: number, weapon: string, heavy: boolean?) → void
One swing's damage for `weapon` ("katana" | "baton"): base + gear, times the damage multiplier and every perk that applies, with a crit roll. Returns damage, crit.
| arg | type | description |
|---|---|---|
| base | number | |
| weapon | string | |
| heavy | boolean? |
weaponElement(weapon: string) → string
The element of the equipped weapon in that slot ("burn" | "shock" | nil).
| arg | type | description |
|---|---|---|
| weapon | string |
itemCount(id: string) → number return profile
Inventory and equipment
| arg | type | description |
|---|---|---|
| id | string |
hasItem(id: string) → boolean return M
| arg | type | description |
|---|---|---|
| id | string |
inventory(kind: string?) → void
{ {id, count, def} } sorted by kind then name; optional kind filter.
| arg | type | description |
|---|---|---|
| kind | string? |
addItem(id: string, count: number?, quiet: boolean?) → boolean
| arg | type | description |
|---|---|---|
| id | string | |
| count | number? | |
| quiet | boolean? |
removeItem(id: string, count: number?) → boolean
| arg | type | description |
|---|---|---|
| id | string | |
| count | number? |
equipped(slot: string) → string
| arg | type | description |
|---|---|---|
| slot | string |
canEquip(id: string) → void
| arg | type | description |
|---|---|---|
| id | string |
equip(id: string) → boolean
| arg | type | description |
|---|---|---|
| id | string |
unequip(slot: string) → void
| arg | type | description |
|---|---|---|
| slot | string |
useItem(id: string) → boolean
Use a consumable. Returns true when it was used up.
| arg | type | description |
|---|---|---|
| id | string |
bestHeal( ) → string
Best healing consumable V carries (for the quick-heal key), or nil.
defineVendor(id: string, def: any) → void
def = { name, cast = castId, stock = { itemId, ... } }
| arg | type | description |
|---|---|---|
| id | string | |
| def | any |
spendEddies(amount: number) → boolean
| arg | type | description |
|---|---|---|
| amount | number |
price(id: string, buying: boolean) → number
What V pays (buying) or is paid (selling) for one of `id`.
| arg | type | description |
|---|---|---|
| id | string | |
| buying | boolean |
buy(id: string, vendorId: string?) → void
| arg | type | description |
|---|---|---|
| id | string | |
| vendorId | string? |
sell(id: string, vendorId: string?) → void
| arg | type | description |
|---|---|---|
| id | string | |
| vendorId | string? |
openShop(vendorId: string) → void
Ask the shops slice to open a vendor's shop.
| arg | type | description |
|---|---|---|
| vendorId | string |
flag(name: string) → any return profile
Flags, counters, affection and romance
| arg | type | description |
|---|---|---|
| name | string |
setFlag(name: string, value: any) → void
| arg | type | description |
|---|---|---|
| name | string | |
| value | any |
counter(name: string) → number return profile
| arg | type | description |
|---|---|---|
| name | string |
bumpCounter(name: string, by: number?) → number
| arg | type | description |
|---|---|---|
| name | string | |
| by | number? |
affection(npc: string) → number return profile
| arg | type | description |
|---|---|---|
| npc | string |
addAffection(npc: string, delta: number, reason: string?) → void
| arg | type | description |
|---|---|---|
| npc | string | |
| delta | number | |
| reason | string? |
giftValue(npc: string, itemId: string) → number
How much a gift moves `npc` (loves 15, likes 6, anything else 2).
| arg | type | description |
|---|---|---|
| npc | string | |
| itemId | string |
giveGift(npc: string, itemId: string) → number
| arg | type | description |
|---|---|---|
| npc | string | |
| itemId | string |
romance( ) → string
setRomance(npc: string?) → void
| arg | type | description |
|---|---|---|
| npc | string? |
defineQuest(def: any) → void
| arg | type | description |
|---|---|---|
| def | any |
qstate(id: string) → any
| arg | type | description |
|---|---|---|
| id | string |
questStatus(id: string) → string
| arg | type | description |
|---|---|---|
| id | string |
questStage(id: string) → string
| arg | type | description |
|---|---|---|
| id | string |
questActive(id: string) → boolean return M
| arg | type | description |
|---|---|---|
| id | string |
questDone(id: string) → boolean return M
| arg | type | description |
|---|---|---|
| id | string |
trackedQuest( ) → string
questLog( ) → void
Every quest V has, as { id, def, status, stage } (active first, by order).
trackQuest(id: string?) → void
| arg | type | description |
|---|---|---|
| id | string? |
retrack( ) → void
startQuest(id: string, stage: string?) → boolean
| arg | type | description |
|---|---|---|
| id | string | |
| stage | string? |
setQuestStage(id: string, stage: string) → boolean
| arg | type | description |
|---|---|---|
| id | string | |
| stage | string |
grant(rewards: any?, reason: string?) → void
Grant a reward table { xp, eddies, items = {[id]=n}, affection = {[npc]=n} }.
| arg | type | description |
|---|---|---|
| rewards | any? | |
| reason | string? |
completeQuest(id: string) → boolean
| arg | type | description |
|---|---|---|
| id | string |
failQuest(id: string) → boolean
| arg | type | description |
|---|---|---|
| id | string |
questStageDef(id: string) → any
The current stage definition of a quest, or nil.
| arg | type | description |
|---|---|---|
| id | string |
setCastPosition(npc: string, pos: any) → void
| arg | type | description |
|---|---|---|
| npc | string | |
| pos | any |
castPosition(npc: string) → any
| arg | type | description |
|---|---|---|
| npc | string |
questWaypoint(id: string?) → any
Where the tracked quest wants V to go: { x, z, label, color } or nil.
| arg | type | description |
|---|---|---|
| id | string? |
reportQuickhack(id: string, targetId: string?) → void
The hacking slice calls this when a quickhack lands on a target.
| arg | type | description |
|---|---|---|
| id | string | |
| targetId | string? |
focus( ) → string
UI focus. While a full-screen interaction is open (dialogue, shop, journal, inventory, breach) the player can't move or fight and enemies hold off.
setFocus(name: string) → boolean
Claim focus for `name`; false when something else holds it.
| arg | type | description |
|---|---|---|
| name | string |
clearFocus(name: string?) → void
| arg | type | description |
|---|---|---|
| name | string? |
canControl( ) → boolean
True when the player may move, fight and interact.
startDialogue(script: any) → boolean
Dialogue (visual novel). The story slice renders it. script = { id, start = key, onEnd = fn?, nodes = { [key] = { speaker = castId | "narrator", mood = "neutral"|"happy"|"angry"|"sad"|"flirty"|"smug", text = string, next = key?, run = fn?, -- run fires when the node shows choices = { { text, next = key?, tag = "[COOL 5]"?, requires = fn()->bool?, run = fn? } }? } } } A node with neither `next` nor `choices` ends the conversation, and so does a choice with no `next`.
| arg | type | description |
|---|---|---|
| script | any |
dialogueOpen( ) → boolean return S
endDialogue(script: any?) → void
The renderer calls this when the conversation closes.
| arg | type | description |
|---|---|---|
| script | any? |
addTalkProvider(npc: string, priority: number, fn: () → void
| arg | type | description |
|---|---|---|
| npc | string | |
| priority | number | |
| fn | ( |
talkTo(npc: string) → boolean
| arg | type | description |
|---|---|---|
| npc | string |
registerInteractable(def: any) → void
| arg | type | description |
|---|---|---|
| def | any |
unregisterInteractable(id: string) → void
| arg | type | description |
|---|---|---|
| id | string |
nearestInteractable(pos: any?) → any
| arg | type | description |
|---|---|---|
| pos | any? |
interact(id: string) → boolean
| arg | type | description |
|---|---|---|
| id | string |
requestEncounter(def: any) → void
| arg | type | description |
|---|---|---|
| def | any |
encounter(id: string) → any
| arg | type | description |
|---|---|---|
| id | string |
encounters( ) → void
cancelEncounter(id: string) → void
| arg | type | description |
|---|---|---|
| id | string |
encounterCleared(id: string) → void
The combat slice calls this when the last enemy of an encounter falls.
| arg | type | description |
|---|---|---|
| id | string |
ram( ) → void
Hacking: RAM, breach protocol and quickhacks.
addRam(n: number) → void
| arg | type | description |
|---|---|---|
| n | number |
spendRam(n: number) → boolean
| arg | type | description |
|---|---|---|
| n | number |
requestBreach(def: any) → boolean
Ask the hacking slice to run a Breach Protocol. def = { id, label, difficulty = 1..3, onResult = fn(success: boolean)? }
| arg | type | description |
|---|---|---|
| def | any |
breachResult(id: string, success: boolean) → void
The hacking slice calls this when the breach ends.
| arg | type | description |
|---|---|---|
| id | string | |
| success | boolean |
applyEffect(target: any, effect: string, params: any?) → boolean
Apply a status effect to a registered target, when it supports one. effect: "stun" {seconds} | "burn" {dps, seconds} | "shock" {damage} | "reveal" {seconds}
| arg | type | description |
|---|---|---|
| target | any | |
| effect | string | |
| params | any? |
discover(placeId: string) → boolean
Places Mark a place discovered (first time: XP and "placeDiscovered").
| arg | type | description |
|---|---|---|
| placeId | string |
perkRank(id: string) → number return profile
Perks: five attribute trees (game.rpg.perks). Tiers open at attribute 4 / 9 / 15 / 20; a level gives a perk point, skill milestones give more.
| arg | type | description |
|---|---|---|
| id | string |
perkPoints( ) → number return profile
perkFlag(name: string) → boolean
True when V owns a perk carrying `flag` (Second Heart, Blade Dancer, ...).
| arg | type | description |
|---|---|---|
| name | string |
canBuyPerk(id: string) → void
Can V buy the next rank of perk `id`? Returns ok, why-not.
| arg | type | description |
|---|---|---|
| id | string |
buyPerk(id: string) → void
| arg | type | description |
|---|---|---|
| id | string |
respecCost( ) → number return 50
Refund every perk point for eddies (attributes stay where they are).
respec( ) → void
skillLevel(id: string) → number
Skills: level by doing (game.rpg.perks skills). Each level adds a passive bonus to R.stat; levels 5 / 10 / 15 / 20 add a perk point.
| arg | type | description |
|---|---|---|
| id | string |
skillXp(id: string) → void
xp into the current level, xp needed for the next.
| arg | type | description |
|---|---|---|
| id | string |
addSkillXp(id: string, amount: number) → void
| arg | type | description |
|---|---|---|
| id | string | |
| amount | number |
credLevel( ) → number return profile
Street cred: its own track. Vendor stock with `cred = n` needs level n.
cred( ) → void
level, xp into it, xp for the next
addCred(amount: number, reason: string?) → void
| arg | type | description |
|---|---|---|
| amount | number | |
| reason | string? |
rarityOf(id: string) → number return tierOf
| arg | type | description |
|---|---|---|
| id | string |
makeGear(baseId: string, r: number?) → string
Roll a new piece of `baseId` at tier r (default: the base's own tier). Defines the item and returns its id; R.addItem(id) gives it to V.
| arg | type | description |
|---|---|---|
| baseId | string | |
| r | number? |
rollLoot(kind: string, level: number?) → void
What a dead enemy leaves besides eddies: item ids to drop (rolled gear is already defined). The boss always drops Epic or better.
| arg | type | description |
|---|---|---|
| kind | string | |
| level | number? |
canDisassemble(id: string) → void
| arg | type | description |
|---|---|---|
| id | string |
disassemble(id: string) → void
Break an item down into crafting components (Engineer skill XP).
| arg | type | description |
|---|---|---|
| id | string |
upgradeCost(id: string) → void
Cost to take `id` up one tier: component id, count, eddies (nil id when it can't go higher).
| arg | type | description |
|---|---|---|
| id | string |
canUpgrade(id: string) → void
| arg | type | description |
|---|---|---|
| id | string |
upgrade(id: string) → void
Upgrade one tier. A rolled piece upgrades in place; a shop item becomes a rolled piece of the next tier (staying equipped). Returns the item id.
| arg | type | description |
|---|---|---|
| id | string |
quickhackCost(id: string) → number
RAM a quickhack costs after perks.
| arg | type | description |
|---|---|---|
| id | string |
dodge(_payload: any) → void
A dodge feeds Shinobi and opens the Blade Dancer crit window.
| arg | type | description |
|---|---|---|
| _payload | any |
Sub-parts
Everything contained inside this part. Assets are composite children (clickable cards). Files are leaf payloads. Expand any row to view its source.
Problems
Everything affecting this asset right now: its own problems, anything wrong inside it, and problems on its direct dependencies.
agent_score is exposed.+ quality × 0.35
+ performance × 0.25
± compat factor
Usability ratings
Did the part work as advertised when consumers tried to drop it in. Separate from upvotes: those are taste; this is "did it function".
Scoped to this part · feeds back into the world's score.