# npc_test.ludic - the census by the hour and by weight, unique names, walking to a spot and never # into the water, a routine's steps by the hour, turning to a player and greeting them once, leaving # out of sight, lines and choices, a post, and a guest's copy import "ludic.npc" import "ludic.base" import "fake" program NpcTest { numbers float bind NpcWorld { ground: fn fk_ground water: fn fk_water allowed: fn fk_allowed push: fn fk_push pushed_x: fn fk_pushed_x pushed_z: fn fk_pushed_z clear: fn fk_clear hour: fn fk_hour_now day: fn fk_day_now players: fn fk_players player_on: fn fk_player_on player_x: fn fk_x player_z: fn fk_z want: fn fk_wanted extra: fn fk_extra_kind taken: fn fk_taken born: fn fk_on_born gone: fn fk_on_gone } bind NpcTalk { say: fn tk_say, greeting: fn tk_greeting } def NpcActs sit { } def NpcActs fish { turns: false } def NpcKinds from "kinds.lres" def NpcNames from "names.lres" def NpcLines from "lines.lres" state NpcTestState { news_on: bool = false } function tk_say(npc_test_st: NpcTestState, p: NpcPerson, line: int) -> string { if npc_test_st.news_on { return `{p.name} saw a bear.` } return "" } function tk_greeting(p: NpcPerson) -> string { return "What'll it be?" } function tk_start(npc_st: mut NpcState, p: NpcPerson) -> void { npc_start_near(npc_st, p, 0.0, 0.0, 0.0, 10.0) } function one(npc_st: mut NpcState, npc_tests_fake_st: mut NpcTestsFakeState, kind: int) -> NpcPerson { fk_setup(npc_st, npc_tests_fake_st) npc_tests_fake_st.fk_want = 1 let p = npc_spawn(npc_st, kind) npc_set_at(p, 0.0, 0.0) return p } test "the tables: the package's acts and spots, and the game's kinds, names and lines" { expect_eq(NPCA_WALK, 0) expect_eq(NPCA_IDLE, 1) expect_eq(npcspots_find("shore_here"), NPCS_SHORE_HERE) expect_eq(NPCK_COUNT, 3) expect(NpcKinds[NPCK_CAMPER].stays) expect_eq(len(NpcKinds[NPCK_HIKER].steps), 3) expect_eq(NPCN_COUNT, 14) } test "the census fills to what the place wants, by weight, and after dark only who stays out" (npc_st: mut NpcState, npc_tests_fake_st: mut NpcTestsFakeState) { fk_setup(npc_st, npc_tests_fake_st) npc_tests_fake_st.fk_want = 4 npc_tick(npc_st, 0.05) expect_eq(npc_count(npc_st), 4) expect_eq(npc_tests_fake_st.fk_born, 4) expect_eq(npc_name_clashes(npc_st), 0) for i in 0 .. len(npc_walkers(npc_st)) { expect(npc_walkers(npc_st)[i].kind != NPCK_VENDOR) } fk_setup(npc_st, npc_tests_fake_st) npc_tests_fake_st.fk_want = 3 npc_tests_fake_st.fk_hour = 23.0 npc_tick(npc_st, 0.05) expect_eq(npc_count(npc_st), 3) for i in 0 .. len(npc_walkers(npc_st)) { expect_eq(npc_walkers(npc_st)[i].kind, NPCK_CAMPER) } } test "no name twice, and never one the place keeps" (npc_st: mut NpcState, npc_tests_fake_st: mut NpcTestsFakeState) { fk_setup(npc_st, npc_tests_fake_st) npc_tests_fake_st.fk_want = 6 npc_tick(npc_st, 0.05) expect_eq(npc_count(npc_st), 6) expect_eq(npc_name_clashes(npc_st), 0) for i in 0 .. 6 { expect(npc_walkers(npc_st)[i].name != "Ada") } } test "it walks to where it is going and begins there, facing what it came for" (npc_st: mut NpcState, npc_tests_fake_st: mut NpcTestsFakeState) { let p = one(npc_st, npc_tests_fake_st, NPCK_HIKER) npc_go(npc_st, p, 20.0, 0.0, NPCA_SIT, 30.0) npc_face(p, 30.0, 0.0) fk_run(npc_st, 30.0) expect_eq(p.act, NPCA_SIT) expect(npc_d(p.x, p.z, 20.0, 0.0) < 1.3) expect(p.phase > 20.0) expect_eq(fk_count(npc_st, NPC_F_ARRIVED), 1) expect_near(p.fx, 30.0, 0.01) } test "a trunk in the way is walked round, and a line through it is not a clear walk" (npc_st: mut NpcState, npc_tests_fake_st: mut NpcTestsFakeState) { let p = one(npc_st, npc_tests_fake_st, NPCK_HIKER) npc_tests_fake_st.fk_trunk = true expect(not npc_line_ok(0.0, 0.0, 20.0, 0.0)) expect(npc_line_ok(0.0, 5.0, 20.0, 5.0)) npc_go(npc_st, p, 20.0, 0.0, NPCA_SIT, 30.0) var closest = 100.0 for k in 0 .. 600 { npc_tick(npc_st, 0.05) closest = Math.min(closest, npc_d(p.x, p.z, 10.0, 0.3)) } expect(closest > 1.3) expect_eq(p.act, NPCA_SIT) } test "nobody walks into the lake, and turning back from it is told as a fallback" (npc_st: mut NpcState, npc_tests_fake_st: mut NpcTestsFakeState) { let p = one(npc_st, npc_tests_fake_st, NPCK_HIKER) npc_go(npc_st, p, -80.0, 0.0, NPCA_SIT, 30.0) var west = 0.0 for k in 0 .. 1200 { npc_tick(npc_st, 0.05) west = Math.min(west, p.x) } expect(west > -50.5) var fell = 0 let fs = q_drain(npc_facts(npc_st)) for i in 0 .. len(fs) { if fs[i].what == NPC_F_STUCK and fs[i].x > -52.0 { fell += 1 } } expect(fell >= 1) } test "a step's spot is where its hours say: the shore by day, home by night" (npc_st: mut NpcState, npc_tests_fake_st: mut NpcTestsFakeState) { let p = one(npc_st, npc_tests_fake_st, NPCK_CAMPER) npc_set_at(p, -20.0, 0.0) npc_tests_fake_st.fk_hour = 12.0 npc_plan(npc_st, p) expect_eq(p.act, NPCA_WALK) expect_eq(p.next_act, NPCA_FISH) expect(p.fx < -50.0) npc_tests_fake_st.fk_hour = 23.0 npc_plan(npc_st, p) expect_eq(p.act, NPCA_SIT) } test "an act is not begun where the place says nobody stops" (npc_st: mut NpcState, npc_tests_fake_st: mut NpcTestsFakeState) { let p = one(npc_st, npc_tests_fake_st, NPCK_HIKER) npc_set_at(p, 40.0, 0.0) npc_do(npc_st, p, NPCA_SIT, 30.0) expect_eq(p.act, NPCA_WALK) expect(fk_allowed(p.tx, p.tz)) } test "it turns to a player who comes near, greets them once, and again after they have gone" (npc_st: mut NpcState, npc_tests_fake_st: mut NpcTestsFakeState) { let p = one(npc_st, npc_tests_fake_st, NPCK_HIKER) npc_do(npc_st, p, NPCA_IDLE, 100.0) p.yaw = 0.0 fk_player_at(npc_tests_fake_st, 0.0, 0.0 - 5.0) fk_run(npc_st, 2.0) expect(Math.abs(p.yaw) < 0.05) fk_player_at(npc_tests_fake_st, 5.0, 0.0) fk_run(npc_st, 3.0) expect(Math.abs(npc_wrap(p.yaw + 1.5707963)) < 0.05) fk_run(npc_st, 1.0) expect_eq(fk_count(npc_st, NPC_F_GREETED), 1) fk_player_at(npc_tests_fake_st, 40.0, 0.0) fk_run(npc_st, 0.5) fk_player_at(npc_tests_fake_st, 5.0, 0.0) fk_run(npc_st, 0.5) expect_eq(fk_count(npc_st, NPC_F_GREETED), 1) } test "a fisher keeps its eyes on the water, whoever is near" (npc_st: mut NpcState, npc_tests_fake_st: mut NpcTestsFakeState) { let p = one(npc_st, npc_tests_fake_st, NPCK_HIKER) npc_do(npc_st, p, NPCA_FISH, 100.0) p.yaw = 0.0 npc_face(p, 0.0, -10.0) fk_player_at(npc_tests_fake_st, 5.0, 0.0) fk_run(npc_st, 2.0) expect(Math.abs(p.yaw) < 0.05) } test "the day ends out of sight, and not in it" (npc_st: mut NpcState, npc_tests_fake_st: mut NpcTestsFakeState) { let p = one(npc_st, npc_tests_fake_st, NPCK_HIKER) npc_do(npc_st, p, NPCA_IDLE, 1000.0) npc_tests_fake_st.fk_hour = 22.5 fk_player_at(npc_tests_fake_st, 20.0, 0.0) fk_run(npc_st, 0.5) expect(p.active) fk_player_at(npc_tests_fake_st, 500.0, 0.0) fk_run(npc_st, 0.5) expect(not p.active) expect_eq(npc_tests_fake_st.fk_gone, 1) let c = one(npc_st, npc_tests_fake_st, NPCK_CAMPER) npc_do(npc_st, c, NPCA_IDLE, 1000.0) npc_tests_fake_st.fk_hour = 22.5 fk_run(npc_st, 0.5) expect(c.active) } test "lines: the highest that has something to say, the story's words, and choices" (npc_st: mut NpcState, npc_test_st: mut NpcTestState, npc_tests_fake_st: mut NpcTestsFakeState) { let p = one(npc_st, npc_tests_fake_st, NPCK_CAMPER) npc_do(npc_st, p, NPCA_FISH, 100.0) expect_eq(npc_talk(npc_st, p, 0), NPCL_HUSH) expect(npc_said(npc_st) == string(k"npc.shh")) expect(npc_talked_today(p)) npc_do(npc_st, p, NPCA_IDLE, 100.0) expect_eq(npc_line_for(npc_st, p), NPCL_HELLO) npc_test_st.news_on = true expect_eq(npc_line_for(npc_st, p), NPCL_NEWS) expect(npc_said(npc_st) == `{p.name} saw a bear.`) npc_test_st.news_on = false expect_eq(npc_choose(npc_st, p, NPCL_HELLO, 0, 0), NPCL_FISH_NO) expect(npc_said(npc_st) == string(k"npc.not_today")) expect_eq(npc_choose(npc_st, p, NPCL_HELLO, 1, 0), -1) let fs = q_drain(npc_facts(npc_st)) expect_eq(len(fs), 3) expect_eq(fs[1].choice, 7) expect(npc_greeting(p) == "What'll it be?") } test "a post stands where it was put, is not counted, and turns back when nobody is near" (npc_st: mut NpcState, npc_tests_fake_st: mut NpcTestsFakeState) { fk_setup(npc_st, npc_tests_fake_st) let v = npc_place(npc_st, NPCK_VENDOR, 0.0, 0.0, 0.0, "Mateo", 5) expect(v.fixed) expect_eq(npc_count(npc_st), 0) fk_player_at(npc_tests_fake_st, 8.0, 0.0) fk_run(npc_st, 3.0) expect(Math.abs(npc_wrap(v.yaw + 1.5707963)) < 0.05) fk_player_at(npc_tests_fake_st, 300.0, 0.0) fk_run(npc_st, 3.0) expect(Math.abs(v.yaw) < 0.05) expect(npc_d(v.x, v.z, 0.0, 0.0) < 0.001) expect(npc_by_id(npc_st, v.id).name == "Mateo") } test "a guest's copy: the host's slot put, heard and eased toward, and not drawn once it goes quiet" (npc_st: mut NpcState, npc_tests_fake_st: mut NpcTestsFakeState) { fk_setup(npc_st, npc_tests_fake_st) npc_tests_fake_st.fk_want = 2 npc_tick(npc_st, 0.05) npc_mirror(npc_st, true) expect_eq(npc_count(npc_st), 0) let p = npc_put(npc_st, 3, 99, NPCK_HIKER, 12, 1.5, "Wren", 10.0, 10.0) expect(p.active) expect_eq(npc_count(npc_st), 1) npc_heard(npc_st, 3, 10.0, 0.0, 10.0, 0.0, 1.2, NPCA_WALK, false) npc_heard(npc_st, 3, 12.0, 0.0, 10.0, 0.0, 1.2, NPCA_WALK, false) for k in 0 .. 40 { npc_mirror_tick(npc_st, 0.05) } expect_near(p.x, 12.0, 0.01) expect(p.phase > 0.0) npc_tick(npc_st, 0.05) expect_near(p.x, 12.0, 0.01) for k in 0 .. 140 { npc_mirror_tick(npc_st, 0.05) } expect(not npc_heard_lately(npc_st, p)) } }