one way exits
This commit is contained in:
		
							parent
							
								
									3bb94c490e
								
							
						
					
					
						commit
						84cfc0f12d
					
				
							
								
								
									
										31
									
								
								roadmap.md
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								roadmap.md
									
									
									
									
									
								
							@ -67,6 +67,37 @@ end)
 | 
			
		||||
 | 
			
		||||
The movement is still generated.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
so the problem with all of this is that the beahvior of the exit is
 | 
			
		||||
supposedly going to change based on how it is contained. however, we only
 | 
			
		||||
re-read the code when the code changes. we're trying to affect game state
 | 
			
		||||
as part of code compilation and that's uncool.
 | 
			
		||||
 | 
			
		||||
a user would create a new exit, put all the finishing touches on it, then move
 | 
			
		||||
it from their person to the room the exit originates from. WITCH would not
 | 
			
		||||
recompile and no containership would update.
 | 
			
		||||
 | 
			
		||||
i think the dream of goes(north, "pub") is donezo. i think the next best
 | 
			
		||||
thing is goes(north, "foyer", "pub"). on execution, the sender is checked;
 | 
			
		||||
if their container is the first room then they are moved to the second
 | 
			
		||||
room. both cases suffer from the double containment problem. that second
 | 
			
		||||
containment *cannot* be updated as part of WITCH compilation.
 | 
			
		||||
 | 
			
		||||
so we're back to two options:
 | 
			
		||||
- one way exits
 | 
			
		||||
- special creation semantics that handle something like double containership
 | 
			
		||||
 | 
			
		||||
in the interest of moving on--and of putting off special top level commands
 | 
			
		||||
that exist outside of WITCH--i want to do one way exits for now. this sucks
 | 
			
		||||
because all the flavor written for an exit has to be duplicated for its pair.
 | 
			
		||||
 | 
			
		||||
some other ideas because i can't let go. what of a variation on the exits map
 | 
			
		||||
where each exit stores a key in its has data about where it goes. this is no
 | 
			
		||||
better than a dynamic handler (it's worse) and does not help the double
 | 
			
		||||
containership problem.
 | 
			
		||||
 | 
			
		||||
give up and do one way exits.
 | 
			
		||||
 | 
			
		||||
## server beta
 | 
			
		||||
 | 
			
		||||
- [x] grpc server
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,7 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"math/rand"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/uuid"
 | 
			
		||||
	"github.com/jackc/pgx/v4/pgxpool"
 | 
			
		||||
@ -30,6 +31,7 @@ type DB interface {
 | 
			
		||||
	// General
 | 
			
		||||
	GetObject(owner, name string) (*Object, error)
 | 
			
		||||
	GetObjectByID(ID int) (*Object, error)
 | 
			
		||||
	SearchObjectsByName(term string) ([]Object, error)
 | 
			
		||||
 | 
			
		||||
	// Defaults
 | 
			
		||||
	Ensure() error
 | 
			
		||||
@ -41,7 +43,8 @@ type DB interface {
 | 
			
		||||
	AvatarBySessionID(string) (*Object, error)
 | 
			
		||||
	BedroomBySessionID(string) (*Object, error)
 | 
			
		||||
	MoveInto(toMove Object, container Object) error
 | 
			
		||||
	Earshot(Object) ([]Object, error)
 | 
			
		||||
	Earshot(vantage Object) ([]Object, error)
 | 
			
		||||
	Resolve(vantage Object, term string) ([]Object, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Account struct {
 | 
			
		||||
@ -488,6 +491,54 @@ func (db *pgDB) GetObject(owner, name string) (obj *Object, err error) {
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (db *pgDB) SearchObjectsByName(term string) ([]Object, error) {
 | 
			
		||||
	ctx := context.Background()
 | 
			
		||||
 | 
			
		||||
	stmt := `
 | 
			
		||||
		SELECT id, avatar, data, owner, script
 | 
			
		||||
		FROM objects
 | 
			
		||||
		WHERE data['name']::varchar LIKE $1 
 | 
			
		||||
	`
 | 
			
		||||
 | 
			
		||||
	rows, err := db.pool.Query(ctx, stmt, "%"+term+"%")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	out := []Object{}
 | 
			
		||||
	for rows.Next() {
 | 
			
		||||
		o := Object{}
 | 
			
		||||
		if err = rows.Scan(
 | 
			
		||||
			&o.ID,
 | 
			
		||||
			&o.Avatar,
 | 
			
		||||
			&o.Data,
 | 
			
		||||
			&o.OwnerID,
 | 
			
		||||
			&o.Script); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		out = append(out, o)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return out, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (db *pgDB) Resolve(vantage Object, term string) ([]Object, error) {
 | 
			
		||||
	stuff, err := db.Earshot(vantage)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	out := []Object{}
 | 
			
		||||
 | 
			
		||||
	for _, o := range stuff {
 | 
			
		||||
		if strings.Contains(o.Data["name"], term) {
 | 
			
		||||
			out = append(out, o)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return out, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (db *pgDB) GetAccountAvatar(account Account) (*Object, error) {
 | 
			
		||||
	ctx := context.Background()
 | 
			
		||||
	obj := &Object{
 | 
			
		||||
 | 
			
		||||
@ -39,9 +39,3 @@ CREATE TABLE contains (
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
CREATE TYPE heading AS ENUM ('north', 'south', 'east', 'west', 'above', 'below');
 | 
			
		||||
 | 
			
		||||
CREATE TABLE exits (
 | 
			
		||||
  startroom integer REFERENCES objects ON DELETE CASCADE,
 | 
			
		||||
  endroom   integer REFERENCES objects ON DELETE CASCADE,
 | 
			
		||||
  direction heading NOT NULL
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,7 @@
 | 
			
		||||
package witch
 | 
			
		||||
 | 
			
		||||
import "fmt"
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	dirEast  = "_DIR_EAST"
 | 
			
		||||
	dirWest  = "_DIR_WEST"
 | 
			
		||||
@ -13,6 +15,10 @@ type Direction struct {
 | 
			
		||||
	raw string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewDirection(raw string) Direction {
 | 
			
		||||
	return Direction{raw: raw}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d Direction) Reverse() Direction {
 | 
			
		||||
	raw := ""
 | 
			
		||||
	switch d.raw {
 | 
			
		||||
@ -29,11 +35,11 @@ func (d Direction) Reverse() Direction {
 | 
			
		||||
	case dirSouth:
 | 
			
		||||
		raw = dirNorth
 | 
			
		||||
	}
 | 
			
		||||
	return Direction{raw: raw}
 | 
			
		||||
	return NewDirection(raw)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NormalizeHuman takes a direction someone might type like "up" or "north" and returns the correct Direction struct
 | 
			
		||||
func NormalizeHuman(humanDir string) Direction {
 | 
			
		||||
// NormalizeDirection takes a direction someone might type like "up" or "north" and returns the correct Direction struct
 | 
			
		||||
func NormalizeDirection(humanDir string) (Direction, error) {
 | 
			
		||||
	raw := ""
 | 
			
		||||
	switch humanDir {
 | 
			
		||||
	case "up":
 | 
			
		||||
@ -50,9 +56,11 @@ func NormalizeHuman(humanDir string) Direction {
 | 
			
		||||
		raw = dirNorth
 | 
			
		||||
	case "south":
 | 
			
		||||
		raw = dirSouth
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		return Direction{}, fmt.Errorf("did not understand direction '%s'", humanDir)
 | 
			
		||||
	}
 | 
			
		||||
	return Direction{raw: raw}
 | 
			
		||||
 | 
			
		||||
	return NewDirection(raw), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Human returns a string form of this direction like "above" or "north"
 | 
			
		||||
@ -74,3 +82,7 @@ func (d Direction) Human() (humanDir string) {
 | 
			
		||||
 | 
			
		||||
	return humanDir
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d Direction) Equals(o Direction) bool {
 | 
			
		||||
	return d.raw == o.raw
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,116 +0,0 @@
 | 
			
		||||
package witch
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
This file contains the definitions of functions that are injected into scope for WITCH scripts. See witch.go's ScriptContext to see how they are actually added to a LuaState.
 | 
			
		||||
 | 
			
		||||
TODO: consider making this (or witch.go) a different package entirely. the `witch` prefix for the function names in this file is a little annoying.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"log"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	lua "github.com/yuin/gopher-lua"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func witchHas(l *lua.LState) int {
 | 
			
		||||
	l.SetGlobal("_has", l.ToTable(1))
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func witchProvides(l *lua.LState) int {
 | 
			
		||||
	// TODO test this manually
 | 
			
		||||
 | 
			
		||||
	verbAndPattern := l.ToString(1)
 | 
			
		||||
	cb := l.ToFunction(2)
 | 
			
		||||
 | 
			
		||||
	split := strings.SplitN(verbAndPattern, " ", 2)
 | 
			
		||||
	verb := split[0]
 | 
			
		||||
	pattern := split[1]
 | 
			
		||||
 | 
			
		||||
	return addPatternHandler(l, verb, pattern, cb)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func witchHears(l *lua.LState) int {
 | 
			
		||||
	pattern := l.ToString(1)
 | 
			
		||||
	cb := l.ToFunction(2)
 | 
			
		||||
	return addPatternHandler(l, "say", pattern, cb)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func witchSees(l *lua.LState) int {
 | 
			
		||||
	pattern := l.ToString(1)
 | 
			
		||||
	cb := l.ToFunction(2)
 | 
			
		||||
 | 
			
		||||
	return addPatternHandler(l, "emote", pattern, cb)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func witchGoes(l *lua.LState) int {
 | 
			
		||||
	// TODO validate direction
 | 
			
		||||
	// TODO convert direction constant to english
 | 
			
		||||
 | 
			
		||||
	direction := l.ToString(1)
 | 
			
		||||
	targetRoom := l.ToString(2)
 | 
			
		||||
 | 
			
		||||
	cb := func(l *lua.LState) int {
 | 
			
		||||
		log.Printf("please move sender to target room '%s'", targetRoom)
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// TODO call addPatternHandler again for the reverse direction (make a reverse helper)
 | 
			
		||||
	return addPatternHandler(l, "go", direction, l.NewFunction(cb))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func witchSeen(l *lua.LState) int {
 | 
			
		||||
	cb := l.ToFunction(1)
 | 
			
		||||
	return addHandler(l, "look", cb)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func witchMy(l *lua.LState) int {
 | 
			
		||||
	hasT := l.GetGlobal("_has").(*lua.LTable)
 | 
			
		||||
	val := hasT.RawGetString(l.ToString(1))
 | 
			
		||||
	l.Push(val)
 | 
			
		||||
	return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func witchDoes(ls *lua.LState) int {
 | 
			
		||||
	// TODO how to feed events back into the server?
 | 
			
		||||
	// it needs to behave like an event showing up in Commands stream
 | 
			
		||||
	// this handler needs a reference to the gateway which has a channel for sending events that the server will see?
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func addHandler(l *lua.LState, verb string, cb *lua.LFunction) int {
 | 
			
		||||
	pattern := ".*"
 | 
			
		||||
 | 
			
		||||
	log.Printf("adding handler: %s %s %#v", verb, pattern, cb)
 | 
			
		||||
 | 
			
		||||
	handlers := l.GetGlobal("_handlers").(*lua.LTable)
 | 
			
		||||
 | 
			
		||||
	verbHandlers, ok := handlers.RawGetString(verb).(*lua.LTable)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		verbHandlers = l.NewTable()
 | 
			
		||||
		handlers.RawSetString(verb, verbHandlers)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	verbHandlers.RawSetString(pattern, cb)
 | 
			
		||||
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func addPatternHandler(l *lua.LState, verb, pattern string, cb *lua.LFunction) int {
 | 
			
		||||
	log.Printf("adding handler: %s %s %#v", verb, string(pattern), cb)
 | 
			
		||||
 | 
			
		||||
	handlers := l.GetGlobal("_handlers").(*lua.LTable)
 | 
			
		||||
 | 
			
		||||
	verbHandlers, ok := handlers.RawGetString(verb).(*lua.LTable)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		verbHandlers = l.NewTable()
 | 
			
		||||
		handlers.RawSetString(verb, verbHandlers)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	verbHandlers.RawSetString(pattern, cb)
 | 
			
		||||
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
@ -10,6 +10,7 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/vilmibm/hermeticum/proto"
 | 
			
		||||
	"github.com/vilmibm/hermeticum/server/db"
 | 
			
		||||
@ -120,6 +121,7 @@ type ScriptContext struct {
 | 
			
		||||
func NewScriptContext(db db.DB, getSend func(string) func(*proto.ClientMessage) error) (*ScriptContext, error) {
 | 
			
		||||
	sc := &ScriptContext{
 | 
			
		||||
		serverAPI: serverAPI{db: db, getSend: getSend},
 | 
			
		||||
		db:        db,
 | 
			
		||||
	}
 | 
			
		||||
	sc.incoming = make(chan VerbContext)
 | 
			
		||||
 | 
			
		||||
@ -130,7 +132,6 @@ func NewScriptContext(db db.DB, getSend func(string) func(*proto.ClientMessage)
 | 
			
		||||
		for {
 | 
			
		||||
			vc = <-sc.incoming
 | 
			
		||||
			if vc.Target.Script != sc.script {
 | 
			
		||||
				// TODO clear this object out of the exits table
 | 
			
		||||
				sc.script = vc.Target.Script
 | 
			
		||||
				l = lua.NewState()
 | 
			
		||||
 | 
			
		||||
@ -145,16 +146,17 @@ func NewScriptContext(db db.DB, getSend func(string) func(*proto.ClientMessage)
 | 
			
		||||
				l.SetGlobal("down", lua.LString(dirBelow))
 | 
			
		||||
 | 
			
		||||
				// witch object behavior functions
 | 
			
		||||
				l.SetGlobal("has", l.NewFunction(witchHas))
 | 
			
		||||
				l.SetGlobal("hears", l.NewFunction(witchHears))
 | 
			
		||||
				l.SetGlobal("sees", l.NewFunction(witchSees))
 | 
			
		||||
				l.SetGlobal("goes", l.NewFunction(witchGoes))
 | 
			
		||||
				l.SetGlobal("seen", l.NewFunction(witchSeen))
 | 
			
		||||
				l.SetGlobal("my", l.NewFunction(witchMy))
 | 
			
		||||
				l.SetGlobal("provides", l.NewFunction(witchProvides))
 | 
			
		||||
				l.SetGlobal("has", l.NewFunction(sc.wHas))
 | 
			
		||||
				l.SetGlobal("hears", l.NewFunction(sc.wHears))
 | 
			
		||||
				l.SetGlobal("sees", l.NewFunction(sc.wSees))
 | 
			
		||||
				l.SetGlobal("goes", l.NewFunction(sc.wGoes))
 | 
			
		||||
				l.SetGlobal("seen", l.NewFunction(sc.wSeen))
 | 
			
		||||
				l.SetGlobal("my", l.NewFunction(sc.wMy))
 | 
			
		||||
				l.SetGlobal("provides", l.NewFunction(sc.wProvides))
 | 
			
		||||
 | 
			
		||||
				// witch helpers
 | 
			
		||||
				l.SetGlobal("_handlers", l.NewTable())
 | 
			
		||||
				l.SetGlobal("_ID", lua.LNumber(vc.Target.ID))
 | 
			
		||||
 | 
			
		||||
				if err := l.DoString(vc.Target.Script); err != nil {
 | 
			
		||||
					log.Printf("error parsing script %s: %s", vc.Target.Script, err.Error())
 | 
			
		||||
@ -187,7 +189,7 @@ func NewScriptContext(db db.DB, getSend func(string) func(*proto.ClientMessage)
 | 
			
		||||
				senderID := int(lua.LVAsNumber(sender.RawGetString("ID")))
 | 
			
		||||
				owner := l.ToString(1)
 | 
			
		||||
				name := l.ToString(2)
 | 
			
		||||
				db := sc.serverAPI.DB()
 | 
			
		||||
				db := sc.db
 | 
			
		||||
				senderObj, err := db.GetObjectByID(senderID)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					log.Println(err.Error())
 | 
			
		||||
@ -224,6 +226,7 @@ func NewScriptContext(db db.DB, getSend func(string) func(*proto.ClientMessage)
 | 
			
		||||
			senderT.RawSetString("ID", lua.LNumber(vc.Sender.ID))
 | 
			
		||||
			l.SetGlobal("sender", senderT)
 | 
			
		||||
			l.SetGlobal("msg", lua.LString(vc.Rest))
 | 
			
		||||
			l.SetGlobal("_SENDERID", lua.LNumber(vc.Sender.ID))
 | 
			
		||||
 | 
			
		||||
			handlers := l.GetGlobal("_handlers").(*lua.LTable)
 | 
			
		||||
			handlers.ForEach(func(k, v lua.LValue) {
 | 
			
		||||
@ -254,3 +257,126 @@ func NewScriptContext(db db.DB, getSend func(string) func(*proto.ClientMessage)
 | 
			
		||||
func (sc *ScriptContext) Handle(vc VerbContext) {
 | 
			
		||||
	sc.incoming <- vc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (sc *ScriptContext) addHandler(l *lua.LState, verb, pattern string, cb *lua.LFunction) {
 | 
			
		||||
	log.Printf("adding handler: %s %s %#v", verb, string(pattern), cb)
 | 
			
		||||
 | 
			
		||||
	handlers := l.GetGlobal("_handlers").(*lua.LTable)
 | 
			
		||||
 | 
			
		||||
	verbHandlers, ok := handlers.RawGetString(verb).(*lua.LTable)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		verbHandlers = l.NewTable()
 | 
			
		||||
		handlers.RawSetString(verb, verbHandlers)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	verbHandlers.RawSetString(pattern, cb)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (sc *ScriptContext) wMy(l *lua.LState) int {
 | 
			
		||||
	hasT := l.GetGlobal("_has").(*lua.LTable)
 | 
			
		||||
	val := hasT.RawGetString(l.ToString(1))
 | 
			
		||||
	l.Push(val)
 | 
			
		||||
	return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (sc *ScriptContext) wHas(l *lua.LState) int {
 | 
			
		||||
	l.SetGlobal("_has", l.ToTable(1))
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (sc *ScriptContext) wHears(l *lua.LState) int {
 | 
			
		||||
	pattern := l.ToString(1)
 | 
			
		||||
	cb := l.ToFunction(2)
 | 
			
		||||
	sc.addHandler(l, "say", pattern, cb)
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (sc *ScriptContext) wSees(l *lua.LState) int {
 | 
			
		||||
	pattern := l.ToString(1)
 | 
			
		||||
	cb := l.ToFunction(2)
 | 
			
		||||
 | 
			
		||||
	sc.addHandler(l, "emote", pattern, cb)
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (sc *ScriptContext) wSeen(l *lua.LState) int {
 | 
			
		||||
	cb := l.ToFunction(1)
 | 
			
		||||
	sc.addHandler(l, "look", ".*", cb)
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (sc *ScriptContext) wDoes(ls *lua.LState) int {
 | 
			
		||||
	// TODO how to feed events back into the server?
 | 
			
		||||
	// it needs to behave like an event showing up in Commands stream
 | 
			
		||||
	// this handler needs a reference to the gateway which has a channel for sending events that the server will see?
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (sc *ScriptContext) wProvides(l *lua.LState) int {
 | 
			
		||||
	// TODO test this manually
 | 
			
		||||
 | 
			
		||||
	verbAndPattern := l.ToString(1)
 | 
			
		||||
	cb := l.ToFunction(2)
 | 
			
		||||
 | 
			
		||||
	split := strings.SplitN(verbAndPattern, " ", 2)
 | 
			
		||||
	verb := split[0]
 | 
			
		||||
	pattern := split[1]
 | 
			
		||||
 | 
			
		||||
	sc.addHandler(l, verb, pattern, cb)
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (sc *ScriptContext) wGoes(l *lua.LState) int {
 | 
			
		||||
	direction := NewDirection(l.ToString(1))
 | 
			
		||||
	targetRoomTerm := l.ToString(2)
 | 
			
		||||
 | 
			
		||||
	log.Printf("GOT DIRECTION %v", direction)
 | 
			
		||||
 | 
			
		||||
	cb := func(l *lua.LState) (ret int) {
 | 
			
		||||
		targetRoomList, err := sc.db.SearchObjectsByName(targetRoomTerm)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Printf("failed to search for target room: %s", err.Error())
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		switch len(targetRoomList) {
 | 
			
		||||
		case 0:
 | 
			
		||||
			log.Printf("failed to find any matching target room. tell player somehow")
 | 
			
		||||
			return
 | 
			
		||||
		case 1:
 | 
			
		||||
			log.Printf("found the target room")
 | 
			
		||||
		default:
 | 
			
		||||
			log.Printf("found too many matching target rooms. tell player somehow")
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		targetRoom := targetRoomList[0]
 | 
			
		||||
		msg := l.GetGlobal("msg").String()
 | 
			
		||||
		normalized, err := NormalizeDirection(msg)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		sender, err := sc.getSenderFromState(l)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Printf("failed to find sender %s", err.Error())
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if normalized.Equals(direction) {
 | 
			
		||||
			log.Printf("MOVING SENDER TO '%s'", targetRoom.Data["name"])
 | 
			
		||||
			// TODO error checking
 | 
			
		||||
			sc.db.MoveInto(*sender, targetRoom)
 | 
			
		||||
			sc.serverAPI.Tell(targetRoom.ID, sender.ID, fmt.Sprintf("you are now in %s", targetRoom.Data["name"]))
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sc.addHandler(l, "go", ".*", l.NewFunction(cb))
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (sc *ScriptContext) getSenderFromState(l *lua.LState) (*db.Object, error) {
 | 
			
		||||
	lsID := lua.LVAsNumber(l.GetGlobal("_SENDERID"))
 | 
			
		||||
 | 
			
		||||
	return sc.db.GetObjectByID(int(lsID))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -173,21 +173,4 @@ allows({
 | 
			
		||||
  execute = "world",
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
-- option 1: fully manual
 | 
			
		||||
 | 
			
		||||
provides("go east", function(args)
 | 
			
		||||
  if sender.where = "gallery" then
 | 
			
		||||
    move_sender("ossuary")
 | 
			
		||||
  end
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
provides("go west", function(args)
 | 
			
		||||
  if sender.where = "ossuary" then
 | 
			
		||||
    move_sender("gallery")
 | 
			
		||||
  end
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
-- option 2: magical helper
 | 
			
		||||
 | 
			
		||||
-- automatically creates the two `go` handlers above
 | 
			
		||||
goes("east", "gallery", "ossuary")
 | 
			
		||||
goes("east", "gallery")
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user