initial commit

This commit is contained in:
2026-05-25 18:09:51 +02:00
commit df7219677e
58 changed files with 3646 additions and 0 deletions

29
domain/build_test.go Normal file
View File

@@ -0,0 +1,29 @@
package domain
import "testing"
// TestBuildValidates is the headless smoke test described in PLAN.md:
// it composes the full game and asks the library to cross-check every
// name reference between managers. No window opens.
func TestBuildValidates(t *testing.T) {
g := Build()
if err := g.Validate(); err != nil {
t.Fatalf("validate: %v", err)
}
for _, name := range []string{"bedroom", "kitchen"} {
if !g.SceneManager.Has(name) {
t.Errorf("missing scene %q", name)
}
}
for _, name := range []string{"key", "beans", "mug"} {
if !g.ItemManager.Has(name) {
t.Errorf("missing item %q", name)
}
}
if !g.DialogueManager.Has("cat_morning") {
t.Errorf("missing dialogue cat_morning")
}
if !g.ScriptManager.Has("intro") || !g.ScriptManager.Has("victory") {
t.Errorf("missing intro/victory script")
}
}

18
domain/character.cat.go Normal file
View File

@@ -0,0 +1,18 @@
package domain
import (
"image/color"
p "pncdsl/pncdsl"
)
func defineCat(g *p.Game) {
g.CharacterManager.Register(p.Character{
Name: "cat",
Sprite: "spr/cat",
Speed: 40,
SpeechColor: color.RGBA{200, 200, 255, 255},
W: 18,
H: 14,
})
}

View File

@@ -0,0 +1,18 @@
package domain
import (
"image/color"
p "pncdsl/pncdsl"
)
func definePlayer(g *p.Game) {
g.CharacterManager.Register(p.Character{
Name: "player",
Sprite: "spr/player",
Speed: 80,
SpeechColor: color.RGBA{255, 230, 160, 255},
W: 14,
H: 28,
})
}

View File

@@ -0,0 +1,45 @@
package domain
import p "pncdsl/pncdsl"
func defineCatMorning(g *p.Game) {
g.DialogueManager.Register(p.Dialogue{
Name: "cat_morning",
Start: "start",
Nodes: []p.DialogueNode{
{
Name: "start",
Lines: []p.DialogueLine{
{Speaker: "cat", Text: "Mióóóóóóóóu. Késő van."},
},
Choices: []p.DialogueChoice{
{
Text: "Mindjárt megy a kávé.",
Actions: []p.Action{p.SetFlag(FlagPromisedCoffee), p.EndDialogue()},
},
{
Text: "Te etted meg a babot?",
Show: p.Not(p.Flag(FlagCupboardOpen)),
Actions: []p.Action{p.GotoNode("beans")},
},
{
Text: "Hagyj békén.",
Actions: []p.Action{p.EndDialogue()},
},
},
},
{
Name: "beans",
Lines: []p.DialogueLine{
{Speaker: "cat", Text: "A szekrényben van. Mindig ott szokott lenni."},
},
Choices: []p.DialogueChoice{
{
Text: "Köszi.",
Actions: []p.Action{p.EndDialogue()},
},
},
},
},
})
}

31
domain/domain.asset.go Normal file
View File

@@ -0,0 +1,31 @@
package domain
import p "pncdsl/pncdsl"
// registerAssets declares every asset the game references. Files don't
// need to exist on disk — the library will substitute deterministic
// colored placeholders for anything missing.
func registerAssets(g *p.Game) {
imgs := []string{
"bg/bedroom", "bg/kitchen",
"spr/key", "spr/beans", "spr/mug",
"spr/player", "spr/cat",
}
for _, name := range imgs {
g.AssetManager.Register(p.Asset{
Name: name,
Path: "assets/" + name + ".png",
Kind: p.AssetImage,
})
}
audios := []string{
"mus/wakeup", "mus/calm", "snd/coffee_done",
}
for _, name := range audios {
g.AssetManager.Register(p.Asset{
Name: name,
Path: "assets/" + name + ".ogg",
Kind: p.AssetAudio,
})
}
}

10
domain/domain.flag.go Normal file
View File

@@ -0,0 +1,10 @@
package domain
const (
FlagKeyTaken = "key_taken"
FlagCupboardOpen = "cupboard_open"
FlagMugTaken = "mug_taken"
FlagCoffeeHasBeans = "coffee_has_beans"
FlagCoffeeHasMug = "coffee_has_mug"
FlagPromisedCoffee = "promised_coffee"
)

30
domain/game.go Normal file
View File

@@ -0,0 +1,30 @@
package domain
import p "pncdsl/pncdsl"
// Build assembles the full Reggeli Kávé game by registering every entity
// into its matching manager on a fresh *Game. The order matters only for
// readability — Validate() runs after Build to cross-check references.
func Build() *p.Game {
g := p.NewGame("Reggeli Kávé", 320, 200)
registerAssets(g)
definePlayer(g)
defineCat(g)
defineKey(g)
defineBeans(g)
defineMug(g)
defineCatMorning(g)
defineIntroScript(g)
defineVictoryScript(g)
defineBedroom(g)
defineKitchen(g)
g.StartAt("bedroom").OnStart(p.RunScript("intro"))
return g
}

11
domain/item.beans.go Normal file
View File

@@ -0,0 +1,11 @@
package domain
import p "pncdsl/pncdsl"
func defineBeans(g *p.Game) {
g.ItemManager.Register(p.Item{
Name: "beans",
Sprite: "spr/beans",
Description: "őrölt kávébab",
})
}

20
domain/item.key.go Normal file
View File

@@ -0,0 +1,20 @@
package domain
import p "pncdsl/pncdsl"
func defineKey(g *p.Game) {
g.ItemManager.Register(p.Item{
Name: "key",
Sprite: "spr/key",
Description: "rozsdás kulcs",
OnUseWith: map[string]p.Action{
"cupboard": p.Seq(
p.Say("player", "Klikk."),
p.SetFlag(FlagCupboardOpen),
p.Give("beans"),
p.TakeAway("key"),
p.Say("player", "Bab van benne. Pont, ami kell."),
),
},
})
}

11
domain/item.mug.go Normal file
View File

@@ -0,0 +1,11 @@
package domain
import p "pncdsl/pncdsl"
func defineMug(g *p.Game) {
g.ItemManager.Register(p.Item{
Name: "mug",
Sprite: "spr/mug",
Description: "kedvenc bögréje",
})
}

60
domain/scene.bedroom.go Normal file
View File

@@ -0,0 +1,60 @@
package domain
import p "pncdsl/pncdsl"
func defineBedroom(g *p.Game) {
g.SceneManager.Register(p.Scene{
Name: "bedroom",
Background: "bg/bedroom",
Music: "mus/wakeup",
Actors: []p.SceneActor{
{CharacterName: "player", At: p.Point{X: 160, Y: 120}},
},
Hotspots: []p.Hotspot{
{
Name: "bed",
Area: p.Rect(20, 70, 90, 60),
Label: "ágy",
OnLook: p.Say("player", "A párnám még meleg."),
OnUse: p.Say("player", "Most keltem fel. Nem alszom vissza."),
},
{
Name: "nightstand",
Area: p.Rect(115, 80, 35, 50),
Label: "éjjeliszekrény",
OnLook: p.If(p.Flag(FlagKeyTaken),
p.Say("player", "Üres a tetején."),
p.Say("player", "Egy rozsdás kulcs az éjjeliszekrényemen."),
),
OnTake: p.If(p.Flag(FlagKeyTaken),
p.Say("player", "Már elvittem."),
p.Seq(
p.Walk("player", p.Point{X: 130, Y: 120}),
p.Give("key"),
p.SetFlag(FlagKeyTaken),
p.Say("player", "Pont jól jöhet."),
),
),
OnUse: p.If(p.Flag(FlagKeyTaken),
p.Say("player", "Már elvittem a kulcsot."),
p.Seq(
p.Walk("player", p.Point{X: 130, Y: 120}),
p.Give("key"),
p.SetFlag(FlagKeyTaken),
p.Say("player", "Megvan a kulcs."),
),
),
},
{
Name: "door",
Area: p.Rect(260, 50, 40, 90),
Label: "ajtó",
OnLook: p.Say("player", "Az ajtó a konyhába vezet."),
OnUse: p.Seq(
p.Walk("player", p.Point{X: 260, Y: 130}),
p.GoTo("kitchen"),
),
},
},
})
}

114
domain/scene.kitchen.go Normal file
View File

@@ -0,0 +1,114 @@
package domain
import p "pncdsl/pncdsl"
func defineKitchen(g *p.Game) {
g.SceneManager.Register(p.Scene{
Name: "kitchen",
Background: "bg/kitchen",
Music: "mus/calm",
Actors: []p.SceneActor{
{CharacterName: "player", At: p.Point{X: 60, Y: 130}},
{CharacterName: "cat", At: p.Point{X: 210, Y: 132}},
},
Hotspots: []p.Hotspot{
{
Name: "cupboard",
Area: p.Rect(30, 40, 80, 80),
Label: "szekrény",
OnLook: p.Say("player", "Konyhaszekrény. Zárva."),
OnUse: p.If(p.Flag(FlagCupboardOpen),
p.Say("player", "Már nyitva van."),
p.If(p.HasItem("key"),
p.Seq(
p.Walk("player", p.Point{X: 70, Y: 130}),
p.Say("player", "Klikk."),
p.SetFlag(FlagCupboardOpen),
p.Give("beans"),
p.TakeAway("key"),
),
p.Say("player", "Zárva. Kéne egy kulcs."),
),
),
OnUseWith: map[string]p.Action{
"key": p.Seq(
p.Walk("player", p.Point{X: 70, Y: 130}),
p.Say("player", "Klikk."),
p.SetFlag(FlagCupboardOpen),
p.Give("beans"),
p.TakeAway("key"),
),
},
},
{
Name: "shelf",
Area: p.Rect(115, 45, 35, 50),
Label: "polc",
OnLook: p.Say("player", "A polc tele bögrékkel."),
OnTake: p.If(p.Flag(FlagMugTaken),
p.Say("player", "Tiszta már mind elfogyott."),
p.Seq(
p.Walk("player", p.Point{X: 125, Y: 130}),
p.Give("mug"),
p.SetFlag(FlagMugTaken),
p.Say("player", "Egy bögre, kérem."),
),
),
OnUse: p.If(p.Flag(FlagMugTaken),
p.Say("player", "Már elvettem egyet."),
p.Seq(
p.Walk("player", p.Point{X: 125, Y: 130}),
p.Give("mug"),
p.SetFlag(FlagMugTaken),
p.Say("player", "Egy bögre, kérem."),
),
),
},
{
Name: "coffee_machine",
Area: p.Rect(160, 75, 50, 55),
Label: "kávéfőző",
OnLook: p.Say("player", "A jó öreg kávéfőző."),
OnUse: p.If(p.And(p.Flag(FlagCoffeeHasBeans), p.Flag(FlagCoffeeHasMug)),
p.RunScript("victory"),
p.Say("player", "Még hiányzik valami. Bab? Bögre?"),
),
OnUseWith: map[string]p.Action{
"beans": p.Seq(
p.Walk("player", p.Point{X: 180, Y: 130}),
p.SetFlag(FlagCoffeeHasBeans),
p.TakeAway("beans"),
p.Say("player", "Bab a helyén."),
p.If(p.And(p.Flag(FlagCoffeeHasBeans), p.Flag(FlagCoffeeHasMug)),
p.RunScript("victory")),
),
"mug": p.Seq(
p.Walk("player", p.Point{X: 180, Y: 130}),
p.SetFlag(FlagCoffeeHasMug),
p.TakeAway("mug"),
p.Say("player", "Bögre a helyén."),
p.If(p.And(p.Flag(FlagCoffeeHasBeans), p.Flag(FlagCoffeeHasMug)),
p.RunScript("victory")),
),
},
},
{
Name: "cat",
Area: p.Rect(195, 118, 35, 22),
Label: "Cirmos",
OnLook: p.Say("player", "Cirmos, a házikedvencem."),
OnTalk: p.RunDialogue("cat_morning"),
},
{
Name: "door",
Area: p.Rect(0, 40, 22, 90),
Label: "ajtó",
OnLook: p.Say("player", "Vissza a hálóba."),
OnUse: p.Seq(
p.Walk("player", p.Point{X: 20, Y: 130}),
p.GoTo("bedroom"),
),
},
},
})
}

15
domain/script.intro.go Normal file
View File

@@ -0,0 +1,15 @@
package domain
import p "pncdsl/pncdsl"
func defineIntroScript(g *p.Game) {
g.ScriptManager.Register(p.Script{
Name: "intro",
Actions: p.Seq(
p.Wait(0.4),
p.Say("player", "Brr, hideg van."),
p.Say("player", "Egy kávé kéne, mielőtt szétszakad a fejem."),
p.Walk("player", p.Point{X: 160, Y: 130}),
),
})
}

17
domain/script.victory.go Normal file
View File

@@ -0,0 +1,17 @@
package domain
import p "pncdsl/pncdsl"
func defineVictoryScript(g *p.Game) {
g.ScriptManager.Register(p.Script{
Name: "victory",
Actions: p.Seq(
p.Say("player", "Készen is van!"),
p.PlaySound("snd/coffee_done"),
p.Wait(0.6),
p.Say("cat", "Mióóóóu. *boldog macska*"),
p.Say("player", "Reggeli kávé: megmentve."),
p.ShowEnd("Vége — kösz, hogy játszottál!"),
),
})
}