feat(packages): ludic.crafting - recipes as an open registry, can / take / refund / make through a pack port, stations through a port, the hold-to-make, a Crafted fact
A recipe is `def CraftRecipes key { out, n, ins: [...], ns: [...], station, minutes, hold,
group, learned }`. The pack is the CraftPack port (count, take, add, room, and `leaves`: what
an item leaves once used, so water gives back its bottle); stations are CraftStations (ready,
start), unbound meaning hands only. craft_make hands a click recipe over or starts a timed one
at its station, refunding when the station refuses; the hold runs from the screen that shows it.
Uses ludic.base only; ten tests with a fake pack and fake stations.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
7f8a7ea7ed
commit
cc9cec922a
9 changed files with 475 additions and 0 deletions
72
packages/ludic.crafting/README.md
Normal file
72
packages/ludic.crafting/README.md
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
# ludic.crafting
|
||||||
|
|
||||||
|
Recipes and making them: what goes in, what comes out, where it is made and how long it takes,
|
||||||
|
whether it can be made right now, taking the makings, giving them back, and a recipe made by
|
||||||
|
holding a button rather than clicking it. Uses [`ludic.base`](../ludic.base/README.md) and
|
||||||
|
nothing else - the pack and the stations are the game's, asked through ports.
|
||||||
|
|
||||||
|
```ludic
|
||||||
|
import "ludic.crafting"
|
||||||
|
```
|
||||||
|
|
||||||
|
## The recipes
|
||||||
|
|
||||||
|
`CraftRecipes` is an `open registry` the game fills, one `def` per recipe; `CRAFT_<KEY>` is its
|
||||||
|
index. A station that holds a recipe holds its index, so a game that saves that appends.
|
||||||
|
|
||||||
|
```ludic
|
||||||
|
def CraftRecipes torch { out: IT_TORCH, ins: [IT_WOOD, IT_CLOTH], ns: [1, 1] }
|
||||||
|
def CraftRecipes rope { out: IT_ROPE, n: 2, ins: [IT_WILLOW], ns: [4], hold: 2.6 }
|
||||||
|
def CraftRecipes stew { out: IT_STEW, ins: [IT_MUSHROOM, IT_WATER], ns: [2, 1], station: POT, minutes: 25 }
|
||||||
|
```
|
||||||
|
|
||||||
|
| field | |
|
||||||
|
| --- | --- |
|
||||||
|
| `out`, `n` | what it makes, and how many |
|
||||||
|
| `ins`, `ns` | the makings and how many of each, side by side |
|
||||||
|
| `station` | where it is made: `CRAFT_HANDS` (0, the default) is anywhere; any other number is the game's |
|
||||||
|
| `minutes` | game minutes at its station (0: made at once); a timed recipe at a station is started there |
|
||||||
|
| `hold` | seconds of holding to make it (0: a click) |
|
||||||
|
| `group` | the game's grouping (a tab, a heading) |
|
||||||
|
| `learned` | false: never made |
|
||||||
|
|
||||||
|
## The ports
|
||||||
|
|
||||||
|
```ludic
|
||||||
|
export port CraftPack {
|
||||||
|
count: fn(int) -> int # how many of an item
|
||||||
|
take: fn(int, int) -> bool
|
||||||
|
add: fn(int, int) -> int
|
||||||
|
room: fn(int) -> int # how many more fit (unbound: no limit)
|
||||||
|
leaves: fn(int) -> int # what an item leaves once used, -1 nothing (unbound: nothing)
|
||||||
|
}
|
||||||
|
export port CraftStations { # unbound: only the hands; nothing can be started
|
||||||
|
ready: fn(int) -> bool # can this station be used right now?
|
||||||
|
start: fn(int, int) -> bool # put recipe in station; false gives the makings back
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
A game with an inventory package binds `CraftPack` to it; this package never names one.
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
| | |
|
||||||
|
| --- | --- |
|
||||||
|
| `craft_count()`, `craft_ok(i)`, `craft_recipe(i)`, `craft_find(item)`, `craft_by_key(key)` | the table |
|
||||||
|
| `craft_inputs(i)`, `craft_input(i, k)`, `craft_input_n(i, k)` | a recipe's makings |
|
||||||
|
| `craft_has(i)` | every making is in the pack |
|
||||||
|
| `craft_can(i)` | learned, the makings, its station ready, and room for what it makes |
|
||||||
|
| `craft_take(i)`, `craft_refund(i)` | the makings out (and what they leave in), and back again |
|
||||||
|
| `craft_make(i) -> bool` | make it: at once into the pack, or started at its station; false and nothing taken when it cannot |
|
||||||
|
| `craft_holds(i)`, `craft_hold_start(i) -> bool`, `craft_hold_stop()`, `craft_holding()`, `craft_hold_frac(i)`, `craft_hold_run(dt) -> bool` | the hold: `run` is true on the frame it is made; it stops itself when the recipe can no longer be made |
|
||||||
|
| `craft_facts() -> Queue<Crafted>` | `{ recipe, item, n, station, queued }`, one per making (`queued`: started at a station) |
|
||||||
|
| `craft_reset()`, `craft_system() -> System` | `"crafting"`, `PH_SIMULATE`: a reset; nothing is saved |
|
||||||
|
|
||||||
|
The hold is run by whatever screen shows it (`craft_hold_run(dt)` from that screen's tick), since a
|
||||||
|
screen may pause the world.
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ludic test packages/ludic.crafting
|
||||||
|
```
|
||||||
42
packages/ludic.crafting/hold.ludic
Normal file
42
packages/ludic.crafting/hold.ludic
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
# hold.ludic - a recipe made by holding a button for its `hold` seconds: letting go wastes
|
||||||
|
# nothing, and it stops by itself the moment it can no longer be made
|
||||||
|
var cr_hold: int = -1
|
||||||
|
var cr_hold_t: float = 0.0
|
||||||
|
|
||||||
|
export function craft_holds(i: int) -> bool { return craft_ok(i) and CraftRecipes[i].hold > 0.0 }
|
||||||
|
|
||||||
|
# the recipe being held, or -1
|
||||||
|
export function craft_holding() -> int { return cr_hold }
|
||||||
|
|
||||||
|
# starts holding one that is made by holding and can be made now
|
||||||
|
export function craft_hold_start(i: int) -> bool {
|
||||||
|
if not craft_holds(i) or not craft_can(i) { return false }
|
||||||
|
cr_hold = i
|
||||||
|
cr_hold_t = 0.0
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
export function craft_hold_stop() -> void {
|
||||||
|
cr_hold = -1
|
||||||
|
cr_hold_t = 0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
# how far through the hold on `i` is, 0 to 1
|
||||||
|
export function craft_hold_frac(i: int) -> float {
|
||||||
|
if cr_hold != i or not craft_holds(i) { return 0.0 }
|
||||||
|
return Math.clamp(cr_hold_t / CraftRecipes[i].hold, 0.0, 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
# `dt` real seconds of holding; true on the frame it is made
|
||||||
|
export function craft_hold_run(dt: float) -> bool {
|
||||||
|
if cr_hold < 0 { return false }
|
||||||
|
if not craft_can(cr_hold) {
|
||||||
|
craft_hold_stop()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
cr_hold_t = cr_hold_t + dt
|
||||||
|
if cr_hold_t <= CraftRecipes[cr_hold].hold { return false }
|
||||||
|
let i = cr_hold
|
||||||
|
craft_hold_stop()
|
||||||
|
return craft_make(i)
|
||||||
|
}
|
||||||
10
packages/ludic.crafting/index.ludic
Normal file
10
packages/ludic.crafting/index.ludic
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
# ludic.crafting - recipes as an open registry; the pack and the stations are the game's, asked
|
||||||
|
# through ports; making one takes its makings and either hands the result over or starts a station
|
||||||
|
module ludic_crafting uses ludic_base
|
||||||
|
numbers float
|
||||||
|
import "ludic.base"
|
||||||
|
import "recipes.ludic"
|
||||||
|
import "ports.ludic"
|
||||||
|
import "verbs.ludic"
|
||||||
|
import "hold.ludic"
|
||||||
|
import "system.ludic"
|
||||||
5
packages/ludic.crafting/package.ludic
Normal file
5
packages/ludic.crafting/package.ludic
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
# ludic.crafting - recipes (what goes in, what comes out, where and how long), whether one can be
|
||||||
|
# made, making it, giving it back, and the hold-to-make. Uses ludic.base and nothing else.
|
||||||
|
package "ludic.crafting"
|
||||||
|
version "0.1.0"
|
||||||
|
kind source
|
||||||
20
packages/ludic.crafting/ports.ludic
Normal file
20
packages/ludic.crafting/ports.ludic
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
# ports.ludic - what crafting asks the game: the pack, and the stations
|
||||||
|
# the pack. `leaves` is what an item leaves behind once used (water leaves its bottle), -1 nothing.
|
||||||
|
export port CraftPack {
|
||||||
|
count: fn(int) -> int
|
||||||
|
take: fn(int, int) -> bool
|
||||||
|
add: fn(int, int) -> int
|
||||||
|
room: fn(int) -> int = fn cr_no_limit
|
||||||
|
leaves: fn(int) -> int = fn cr_leaves_nothing
|
||||||
|
}
|
||||||
|
function cr_no_limit(it: int) -> int { return 2147483647 }
|
||||||
|
function cr_leaves_nothing(it: int) -> int { return -1 }
|
||||||
|
|
||||||
|
# the stations. Unbound, there are none: only the hands are ready, and nothing can be started.
|
||||||
|
# `start(station, recipe)` puts a timed recipe in; false gives the makings back.
|
||||||
|
export port CraftStations {
|
||||||
|
ready: fn(int) -> bool = fn cr_hands_only
|
||||||
|
start: fn(int, int) -> bool = fn cr_no_start
|
||||||
|
}
|
||||||
|
function cr_hands_only(station: int) -> bool { return station == CRAFT_HANDS }
|
||||||
|
function cr_no_start(station: int, recipe: int) -> bool { return false }
|
||||||
59
packages/ludic.crafting/recipes.ludic
Normal file
59
packages/ludic.crafting/recipes.ludic
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
# recipes.ludic - a recipe, the registry the game fills, and the fact a making leaves
|
||||||
|
# station 0 is the hands: anywhere, always ready
|
||||||
|
export const CRAFT_HANDS: int = 0
|
||||||
|
|
||||||
|
# `ins` are items and `ns` how many of each, side by side; `minutes` is game time at a station
|
||||||
|
# (0 is made at once) and `hold` seconds of holding the button (0 is a click)
|
||||||
|
export property CraftRecipe {
|
||||||
|
key: string = ""
|
||||||
|
out: int = 0
|
||||||
|
n: int = 1
|
||||||
|
ins: []int = null
|
||||||
|
ns: []int = null
|
||||||
|
station: int = 0
|
||||||
|
minutes: int = 0
|
||||||
|
hold: float = 0.0
|
||||||
|
group: int = 0
|
||||||
|
learned: bool = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# the game fills it: `def CraftRecipes torch { out: IT_TORCH, ins: [IT_WOOD], ns: [1] }`. A
|
||||||
|
# station holds a recipe by its index, so a game that saves that appends, never inserts.
|
||||||
|
export open registry CraftRecipes of CraftRecipe as CRAFT
|
||||||
|
|
||||||
|
# a recipe was made: handed over (`queued` false) or put into its station (`queued` true)
|
||||||
|
export property Crafted {
|
||||||
|
recipe: int = 0
|
||||||
|
item: int = 0
|
||||||
|
n: int = 0
|
||||||
|
station: int = 0
|
||||||
|
queued: bool = false
|
||||||
|
}
|
||||||
|
|
||||||
|
export function craft_ok(i: int) -> bool { return i >= 0 and i < CRAFT_COUNT }
|
||||||
|
|
||||||
|
export function craft_count() -> int { return CRAFT_COUNT }
|
||||||
|
|
||||||
|
export function craft_recipe(i: int) -> CraftRecipe { return CraftRecipes[i] }
|
||||||
|
|
||||||
|
# how many makings a recipe has
|
||||||
|
export function craft_inputs(i: int) -> int {
|
||||||
|
if not craft_ok(i) or CraftRecipes[i].ins == null { return 0 }
|
||||||
|
return len(CraftRecipes[i].ins)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function craft_input(i: int, k: int) -> int { return CraftRecipes[i].ins[k] }
|
||||||
|
|
||||||
|
export function craft_input_n(i: int, k: int) -> int { return CraftRecipes[i].ns[k] }
|
||||||
|
|
||||||
|
# the first recipe that makes `item`, or -1
|
||||||
|
export function craft_find(item: int) -> int {
|
||||||
|
for i in 0 .. CRAFT_COUNT { if CraftRecipes[i].out == item { return i } }
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
# the recipe the save calls `key`, or -1
|
||||||
|
export function craft_by_key(key: string) -> int {
|
||||||
|
for i in 0 .. CRAFT_COUNT { if CraftRecipes[i].key == key { return i } }
|
||||||
|
return -1
|
||||||
|
}
|
||||||
12
packages/ludic.crafting/system.ludic
Normal file
12
packages/ludic.crafting/system.ludic
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
# system.ludic - crafting as a system: a reset, and nothing saved (a hold never outlives its
|
||||||
|
# screen; what a station holds is the station's)
|
||||||
|
export function craft_reset() -> void {
|
||||||
|
craft_hold_stop()
|
||||||
|
q_clear(craft_facts())
|
||||||
|
}
|
||||||
|
|
||||||
|
export function craft_system() -> System {
|
||||||
|
let s = system_new("crafting", PH_SIMULATE)
|
||||||
|
s.reset = fn craft_reset
|
||||||
|
return s
|
||||||
|
}
|
||||||
178
packages/ludic.crafting/tests/crafting_test.ludic
Normal file
178
packages/ludic.crafting/tests/crafting_test.ludic
Normal file
|
|
@ -0,0 +1,178 @@
|
||||||
|
# crafting_test.ludic - a recipe made from a fake pack: the makings, the station's say, room for
|
||||||
|
# the result, what water leaves behind, a station that refuses, the hold, and the facts
|
||||||
|
import "ludic.crafting"
|
||||||
|
import "ludic.base"
|
||||||
|
program CraftingTest {
|
||||||
|
numbers float
|
||||||
|
const WOOD: int = 0
|
||||||
|
const CLOTH: int = 1
|
||||||
|
const TORCH: int = 2
|
||||||
|
const ROPE: int = 3
|
||||||
|
const WATER: int = 4
|
||||||
|
const BOTTLE: int = 5
|
||||||
|
const TEA: int = 6
|
||||||
|
const HERB: int = 7
|
||||||
|
const BENCH: int = 1
|
||||||
|
const POT: int = 2
|
||||||
|
|
||||||
|
def CraftRecipes torch { out: TORCH, ins: [WOOD, CLOTH], ns: [1, 1] }
|
||||||
|
def CraftRecipes rope { out: ROPE, n: 2, ins: [HERB], ns: [4], hold: 2.0 }
|
||||||
|
def CraftRecipes tea { out: TEA, ins: [HERB, WATER], ns: [2, 1], station: POT, minutes: 8 }
|
||||||
|
def CraftRecipes chair { out: TORCH, ins: [WOOD], ns: [3], station: BENCH }
|
||||||
|
def CraftRecipes secret { out: TORCH, ins: [WOOD], ns: [1], learned: false }
|
||||||
|
|
||||||
|
var pack: []int = null
|
||||||
|
var bench_near: bool = true
|
||||||
|
var pot_takes: bool = true
|
||||||
|
var pot_got: int = -1
|
||||||
|
|
||||||
|
function count(it: int) -> int { return pack[it] }
|
||||||
|
function take(it: int, n: int) -> bool {
|
||||||
|
if pack[it] < n { return false }
|
||||||
|
pack[it] = pack[it] - n
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
function add(it: int, n: int) -> int {
|
||||||
|
pack[it] = pack[it] + n
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
function room(it: int) -> int {
|
||||||
|
if it == TORCH { return 3 - pack[it] }
|
||||||
|
return 99
|
||||||
|
}
|
||||||
|
function leaves(it: int) -> int {
|
||||||
|
if it == WATER { return BOTTLE }
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
function ready(st: int) -> bool {
|
||||||
|
if st == BENCH { return bench_near }
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
function start(st: int, recipe: int) -> bool {
|
||||||
|
if not pot_takes { return false }
|
||||||
|
pot_got = recipe
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
bind CraftPack { count: fn count, take: fn take, add: fn add, room: fn room, leaves: fn leaves }
|
||||||
|
bind CraftStations { ready: fn ready, start: fn start }
|
||||||
|
|
||||||
|
function fresh() -> void {
|
||||||
|
pack = new []int
|
||||||
|
for i in 0 .. 8 { push(pack, 0) }
|
||||||
|
craft_reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
function facts() -> []Crafted { return q_drain(craft_facts()) }
|
||||||
|
|
||||||
|
test "the registry is the game's, in the order written" {
|
||||||
|
expect_eq(CRAFT_COUNT, 5)
|
||||||
|
expect_eq(CRAFT_TEA, 2)
|
||||||
|
expect_eq(craft_find(ROPE), CRAFT_ROPE)
|
||||||
|
expect_eq(craft_by_key("chair"), CRAFT_CHAIR)
|
||||||
|
expect_eq(craft_inputs(CRAFT_TEA), 2)
|
||||||
|
expect_eq(craft_find(99), -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
test "made at once from the makings, and said" {
|
||||||
|
fresh()
|
||||||
|
expect(not craft_can(CRAFT_TORCH))
|
||||||
|
pack[WOOD] = 2
|
||||||
|
pack[CLOTH] = 1
|
||||||
|
expect(craft_make(CRAFT_TORCH))
|
||||||
|
expect_eq(pack[WOOD], 1)
|
||||||
|
expect_eq(pack[CLOTH], 0)
|
||||||
|
expect_eq(pack[TORCH], 1)
|
||||||
|
let fs = facts()
|
||||||
|
expect_eq(len(fs), 1)
|
||||||
|
expect_eq(fs[0].item, TORCH)
|
||||||
|
expect(not fs[0].queued)
|
||||||
|
}
|
||||||
|
|
||||||
|
test "nothing is taken when it cannot be made" {
|
||||||
|
fresh()
|
||||||
|
pack[WOOD] = 1
|
||||||
|
expect(not craft_make(CRAFT_TORCH))
|
||||||
|
expect_eq(pack[WOOD], 1)
|
||||||
|
expect_eq(len(facts()), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
test "the station decides, and so does the room for what it makes" {
|
||||||
|
fresh()
|
||||||
|
pack[WOOD] = 9
|
||||||
|
bench_near = false
|
||||||
|
expect(craft_has(CRAFT_CHAIR))
|
||||||
|
expect(not craft_can(CRAFT_CHAIR))
|
||||||
|
bench_near = true
|
||||||
|
expect(craft_can(CRAFT_CHAIR))
|
||||||
|
pack[TORCH] = 3
|
||||||
|
expect(not craft_can(CRAFT_CHAIR))
|
||||||
|
}
|
||||||
|
|
||||||
|
test "an unlearned recipe is never made" {
|
||||||
|
fresh()
|
||||||
|
pack[WOOD] = 5
|
||||||
|
expect(not craft_can(CRAFT_SECRET))
|
||||||
|
}
|
||||||
|
|
||||||
|
test "a timed recipe goes into its station, and water leaves its bottle" {
|
||||||
|
fresh()
|
||||||
|
pack[HERB] = 2
|
||||||
|
pack[WATER] = 1
|
||||||
|
expect(craft_make(CRAFT_TEA))
|
||||||
|
expect_eq(pot_got, CRAFT_TEA)
|
||||||
|
expect_eq(pack[TEA], 0)
|
||||||
|
expect_eq(pack[WATER], 0)
|
||||||
|
expect_eq(pack[BOTTLE], 1)
|
||||||
|
let fs = facts()
|
||||||
|
expect(fs[0].queued)
|
||||||
|
expect_eq(fs[0].station, POT)
|
||||||
|
}
|
||||||
|
|
||||||
|
test "a station that says no gives everything back" {
|
||||||
|
fresh()
|
||||||
|
pot_takes = false
|
||||||
|
pack[HERB] = 2
|
||||||
|
pack[WATER] = 1
|
||||||
|
expect(not craft_make(CRAFT_TEA))
|
||||||
|
expect_eq(pack[HERB], 2)
|
||||||
|
expect_eq(pack[WATER], 1)
|
||||||
|
expect_eq(pack[BOTTLE], 0)
|
||||||
|
expect_eq(len(facts()), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
test "a hold makes it after its seconds, and not before" {
|
||||||
|
fresh()
|
||||||
|
pack[HERB] = 4
|
||||||
|
expect(craft_holds(CRAFT_ROPE))
|
||||||
|
expect(not craft_holds(CRAFT_TORCH))
|
||||||
|
expect(craft_hold_start(CRAFT_ROPE))
|
||||||
|
expect(not craft_hold_run(1.0))
|
||||||
|
expect(craft_hold_frac(CRAFT_ROPE) == 0.5)
|
||||||
|
expect(not craft_hold_run(0.9))
|
||||||
|
expect(craft_hold_run(0.2))
|
||||||
|
expect_eq(pack[ROPE], 2)
|
||||||
|
expect_eq(craft_holding(), -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
test "letting go wastes nothing, and losing the makings stops the hold" {
|
||||||
|
fresh()
|
||||||
|
pack[HERB] = 4
|
||||||
|
craft_hold_start(CRAFT_ROPE)
|
||||||
|
craft_hold_run(1.5)
|
||||||
|
craft_hold_stop()
|
||||||
|
expect_eq(pack[HERB], 4)
|
||||||
|
craft_hold_start(CRAFT_ROPE)
|
||||||
|
pack[HERB] = 3
|
||||||
|
expect(not craft_hold_run(5.0))
|
||||||
|
expect_eq(craft_holding(), -1)
|
||||||
|
expect_eq(pack[ROPE], 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
test "a click recipe cannot be held" {
|
||||||
|
fresh()
|
||||||
|
pack[WOOD] = 1
|
||||||
|
pack[CLOTH] = 1
|
||||||
|
expect(not craft_hold_start(CRAFT_TORCH))
|
||||||
|
}
|
||||||
|
}
|
||||||
77
packages/ludic.crafting/verbs.ludic
Normal file
77
packages/ludic.crafting/verbs.ludic
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
# verbs.ludic - can it be made, taking and giving back its makings, and making it
|
||||||
|
var cr_facts: Queue<Crafted> = null
|
||||||
|
|
||||||
|
export function craft_facts() -> Queue<Crafted> {
|
||||||
|
if cr_facts == null { cr_facts = queue_new("crafting.crafted") }
|
||||||
|
return cr_facts
|
||||||
|
}
|
||||||
|
|
||||||
|
# every making is in the pack
|
||||||
|
export function craft_has(i: int) -> bool {
|
||||||
|
if not craft_ok(i) { return false }
|
||||||
|
for k in 0 .. craft_inputs(i) {
|
||||||
|
if CraftPack.count(craft_input(i, k)) < craft_input_n(i, k) { return false }
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
# learned, the makings in the pack, its station ready, and room for what it makes
|
||||||
|
export function craft_can(i: int) -> bool {
|
||||||
|
if not craft_ok(i) { return false }
|
||||||
|
let r = CraftRecipes[i]
|
||||||
|
if not r.learned or not craft_has(i) { return false }
|
||||||
|
if not CraftStations.ready(r.station) { return false }
|
||||||
|
return CraftPack.room(r.out) >= r.n
|
||||||
|
}
|
||||||
|
|
||||||
|
# the makings out of the pack, and what they leave behind into it
|
||||||
|
export function craft_take(i: int) -> void {
|
||||||
|
for k in 0 .. craft_inputs(i) {
|
||||||
|
let it = craft_input(i, k)
|
||||||
|
let n = craft_input_n(i, k)
|
||||||
|
CraftPack.take(it, n)
|
||||||
|
let left = CraftPack.leaves(it)
|
||||||
|
if left >= 0 { CraftPack.add(left, n) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# the makings back, and what they left behind out again (a station said no after all)
|
||||||
|
export function craft_refund(i: int) -> void {
|
||||||
|
for k in 0 .. craft_inputs(i) {
|
||||||
|
let it = craft_input(i, k)
|
||||||
|
let n = craft_input_n(i, k)
|
||||||
|
CraftPack.add(it, n)
|
||||||
|
let left = CraftPack.leaves(it)
|
||||||
|
if left >= 0 { CraftPack.take(left, n) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cr_fact(i: int, queued: bool) -> void {
|
||||||
|
let r = CraftRecipes[i]
|
||||||
|
let c = new Crafted
|
||||||
|
c.recipe = i
|
||||||
|
c.item = r.out
|
||||||
|
c.n = r.n
|
||||||
|
c.station = r.station
|
||||||
|
c.queued = queued
|
||||||
|
q_push(craft_facts(), c)
|
||||||
|
}
|
||||||
|
|
||||||
|
# make it: a timed recipe at a station goes into the station (given back if it will not take it),
|
||||||
|
# anything else is handed over at once. False, and the pack untouched, when it cannot be made.
|
||||||
|
export function craft_make(i: int) -> bool {
|
||||||
|
if not craft_can(i) { return false }
|
||||||
|
let r = CraftRecipes[i]
|
||||||
|
craft_take(i)
|
||||||
|
if r.minutes > 0 and r.station != CRAFT_HANDS {
|
||||||
|
if not CraftStations.start(r.station, i) {
|
||||||
|
craft_refund(i)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
cr_fact(i, true)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
CraftPack.add(r.out, r.n)
|
||||||
|
cr_fact(i, false)
|
||||||
|
return true
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue