Browse Source

Working on character creation, loading and saving.

Beoran 8 years ago
parent
commit
6ff88ee1fe

+ 2 - 1
.gitignore

@@ -28,4 +28,5 @@ build
 doc/nd
 doc/nd_project
 .tup
-woe.log
+*.log
+*.sitef

+ 110 - 32
src/woe/server/ask.go

@@ -10,9 +10,10 @@ import "github.com/beoran/woe/monolog"
 import "bytes"
 import "regexp"
 // import "fmt"
-// import "strconv"
-
+import "strconv"
+// import "strings"
 
+const NEW_CHARACTER_PRICE = 4
   
 // Switches to "password" mode.
 func (me * Client) PasswordMode() telnet.Event {
@@ -103,23 +104,76 @@ func (me * Client) AskSomething(prompt string, re string, nomatch_prompt string,
       me.NormalMode()
       me.Printf("\n")
     }
-    
+        
     return something
-  }
-  
+}
 
-const LOGIN_RE = "^[A-Za-z][A-Za-z0-9]+$"
+func (me * Client) AskYesNo(prompt string) bool {
+    res := me.AskSomething(prompt + " (y/n)","[ynYN]", "Please answer y or n.", false)
+    if res[0] == 'Y'|| res[0] == 'y' { 
+        return true
+    } else {
+        return false
+    }    
+}
+
+func (me * Client) AskEntityListOnce(heading string, prompt string, noecho bool, elist world.EntitylikeSlice) (result world.Entitylike) { 
+    list := elist.FilterPrivilege(me.account.Privilege)
+    me.Printf("\n%s\n\n",heading)
+    for i, v := range(list) {
+        e := v.AsEntity()
+        me.Printf("[%d] %s: %s\n", i+1, e.Name, e.Short)
+    }
+    me.Printf("\n")
+    aid := me.AskSomething(prompt, "", "", false);
+    iresp, err := strconv.Atoi(string(aid))
+    if err != nil { /* Try name. */
+        e := list.FindName(string(aid))
+        if e != nil {
+            return e
+        } else {
+            me.Printf("Name not found in list. Please choose a number or name from the list above.\n")
+        }
+    } else if (iresp>0) && (iresp<=len(list)) { /* In range. */
+        return list[iresp-1]
+    } else {
+        me.Printf("Please choose a number or name from the list above.\n")
+    }
+    return nil
+}
+    
+
+func (me * Client) AskEntityList(heading string, prompt string, noecho bool, list world.EntitylikeSlice) (result world.Entitylike) {     
+    for {
+        result = me.AskEntityListOnce(heading, prompt, noecho, list)
+        if result != nil {
+            e := result.AsEntity()
+            me.Printf("\n%s: %s\n\n%s\n\n", e.Name, e.Short, e.Long)
+            if noecho || me.AskYesNo("Confirm?") {
+                return result
+            }
+        }
+    }
+}
+    
+
+const LOGIN_RE = "^[A-Za-z]+$"
 
 func (me * Client) AskLogin() []byte {
     return me.AskSomething("Login", LOGIN_RE, "Login must consist of a letter followed by letters or numbers.", false)
 }
 
+
 const EMAIL_RE = "@"
 
 func (me * Client) AskEmail() []byte {
     return me.AskSomething("E-mail", EMAIL_RE, "Email must have at least an @ in there somewhere.", false)
 }
 
+func (me * Client) AskCharacterName() []byte {
+    return me.AskSomething("Character Name", LOGIN_RE, "Character name consisst of letters only.", false)
+}
+
 func (me * Client) AskPassword() []byte {
     return me.AskSomething("Password", "", "", true)
 }
@@ -131,20 +185,6 @@ func (me * Client) AskRepeatPassword() []byte {
 func (me * Client) HandleCommand() {
     command := me.ReadCommand()
     me.ProcessCommand(command)
-    /*
-    if bytes.HasPrefix(command, []byte("/quit")) {
-      me.Printf("Byebye!\n")
-      me.alive = false
-    } else if bytes.HasPrefix(command, []byte("/shutdown")) {
-      me.server.Broadcast("Shutting down server NOW!\n")
-      me.server.Shutdown();
-    } else if bytes.HasPrefix(command, []byte("/restart")) {
-      me.server.Broadcast("Restarting down server NOW!\n")
-      me.server.Restart();
-    } else {
-      me.server.Broadcast("Client %d said %s\r\n", me.id, command)  
-    }
-    */
 }
  
 func (me * Client) ExistingAccountDialog() bool {
@@ -212,7 +252,7 @@ func (me * Client) AccountDialog() bool {
         return false 
     }
     
-    me.account, err = me.server.World.LoadAccount(me.server.DataPath(), string(login))    
+    me.account, err = me.server.World.LoadAccount(string(login))    
     if err != nil {
         monolog.Warning("Could not load account %s: %v", login, err)  
     }
@@ -222,19 +262,57 @@ func (me * Client) AccountDialog() bool {
       return me.NewAccountDialog(string(login))
     }
 }
+
+func (me * Client) NewCharacterDialog() bool {
+    me.Printf("New character:\n")
+    charname := me.AskCharacterName()
+    
+    kin := me.AskEntityList("Please choose the kin of this character", "Kin of character? ", false, world.KinEntityList)
+    me.Printf("%s %v\n", charname, kin)
+     
+    gender := me.AskEntityList("Please choose the gender of this character", "Gender? ", false, world.GenderList)
+    me.Printf("%s %v\n", charname, gender)
+
+    job := me.AskEntityList("Please choose the job of this character", "Job? ", false, world.JobEntityList)
+    me.Printf("%s %v\n", charname, job)
+    
+    character := world.NewCharacter(me.account, 
+                    string(charname), kin, gender, job)
+    
+    me.Printf("%s", character.Being.ToStatus());
+    
+    ok := me.AskYesNo("Is this character ok?")
+    
+    if (!ok) {
+        me.Printf("Character creation canceled.\n")
+        return true
+    }
+    
+    me.account.AddCharacter(character)
+    me.account.Points -= NEW_CHARACTER_PRICE 
+    me.account.Save(me.server.DataPath())
+    character.Save(me.server.DataPath())
+    me.Printf("Character %s saved.\n", character.Being.Name)
+    
+
+    return true
+}    
+
  
 func (me * Client) CharacterDialog() bool {
-    login  := me.AskLogin()
-    if login == nil { return false }
-    var err error
-    me.account, err = world.LoadAccount(me.server.DataPath(), string(login))    
-    if err != nil {
-        monolog.Warning("Could not load account %s: %v", login, err)  
-    }
-    if me.account != nil {
-      return me.ExistingAccountDialog()
-    } else {
-      return me.NewAccountDialog(string(login))
+    me.Printf("You have %d remaining points.\n", me.account.Points)
+    for me.account.NumCharacters() < 1 {
+        me.Printf("You have no characters yet!\n")
+        if (me.account.Points > 0) {
+            me.NewCharacterDialog();
+        } else {
+            me.Printf("Sorry, you have no points left to make new characters!\n")
+            me.Printf("Please contact the staff of WOE if you think this is a mistake.\n")
+            me.Printf("Disconnecting!\n")
+            return false 
+        }
     }
+    return true
 }
+ 
 

+ 18 - 0
src/woe/server/client.go

@@ -116,6 +116,13 @@ func (me * Client) TryReadEvent(millis int) (event telnet.Event, timeout bool, c
     }
 }
 
+func (me * Client) HandleNAWSEvent(nawsevent * telnet.NAWSEvent) {
+    me.info.w = nawsevent.W
+    me.info.h = nawsevent.H
+    monolog.Info("Client %d window size #{%d}x#{%d}", me.id, me.info.w, me.info.h) 
+    me.info.naws     = true    
+}
+
 func (me * Client) TryRead(millis int) (data []byte, timeout bool, close bool) {
     
     for (me.alive) { 
@@ -127,6 +134,8 @@ func (me * Client) TryRead(millis int) (data []byte, timeout bool, close bool) {
             case * telnet.DataEvent:
                 monolog.Debug("Telnet data event %T : %d.", event, len(event.Data))
                 return event.Data, false, false
+            case * telnet.NAWSEvent:
+                me.HandleNAWSEvent(event);
             default:
                 monolog.Info("Ignoring telnet event %T : %v for now.", event, event)
         }
@@ -152,6 +161,15 @@ func (me * Client) Serve() (err error) {
         return nil
     }
     
+    if (!me.CharacterDialog()) {
+        time.Sleep(3); 
+        // sleep so output gets flushed, hopefully.
+        // Also slow down brute force attacks.
+        me.Close()
+        return nil
+    }
+    
+    
     me.Printf("Welcome, %s\n", me.account.Name)
     
     for (me.alive) {

+ 1 - 1
src/woe/server/server.go

@@ -119,7 +119,7 @@ func (me * Server) SetupWorld() (err error) {
     
     if me.World == nil {
         monolog.Info("Creating new default world...")
-        me.World = world.NewWorld("WOE", DEFAULT_MOTD)
+        me.World = world.NewWorld("WOE", DEFAULT_MOTD, me.DataPath())
         err := me.World.Save(me.DataPath())
         if err != nil {
             monolog.Error("Could not save world: %v", err)

+ 64 - 1
src/woe/sitef/sitef.go

@@ -7,6 +7,10 @@ import "fmt"
 import "bytes"
 import "bufio"
 import "strconv"
+import "reflect"
+import "errors"
+import "github.com/beoran/woe/monolog"
+
 
 // Sitef format for serialization
 // Sitef is a simple text format for serializing data to
@@ -56,13 +60,24 @@ func (me * Record) PutInt(key string, val int) {
     me.Putf(key, "%d", val)
 }
 
+
+func (me * Record) PutInt64(key string, val int64) {
+    me.Putf(key, "%d", val)
+}
+
 func (me * Record) PutFloat64(key string, val float64) {
     me.Putf(key, "%lf", val)
 }
 
+func (me Record) MayGet(key string) (result string, ok bool) {
+    result, ok = me[key]
+    return result, ok
+}
+
 
 func (me Record) Get(key string) (result string) {
-    return me[key]
+    result= me[key]
+    return result
 }
 
 func (me Record) Getf(key string, format string, 
@@ -93,6 +108,54 @@ func (me Record) GetFloat(key string) (val float64, error error) {
     return strconv.ParseFloat(me.Get(key), 64)
 }
 
+func (me * Record) convSimple(typ reflect.Type, val reflect.Value) (res string, err error) {
+    switch val.Kind() {
+    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+    return strconv.FormatInt(val.Int(), 10), nil
+        
+    case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+    return strconv.FormatUint(val.Uint(), 10), nil
+    
+    case reflect.Float32, reflect.Float64:
+    return strconv.FormatFloat(val.Float(), 'g', -1, val.Type().Bits()), nil
+    case reflect.String:
+    return val.String(), nil
+    case reflect.Bool:
+    return strconv.FormatBool(val.Bool()), nil
+    default: 
+    return "", errors.New("Unsupported type")
+    }
+}
+
+
+func (me Record) PutValue(key string, value reflect.Value) {
+    switch (value.Kind()) {
+        case reflect.Int, reflect.Int32, reflect.Int64:
+            me.Putf(key, "%d", value.Int())
+        case reflect.Uint, reflect.Uint32, reflect.Uint64:
+            me.Putf(key, "%d", value.Uint())
+        case reflect.Float32, reflect.Float64:
+            me.Putf(key, "%f", value.Float())
+        case reflect.String:
+            me.Putf(key, "%s", value.String())
+        default:
+            me.Put(key, "???")
+    }
+}
+
+func (me Record) PutStruct(prefix string, structure interface {}) {
+    st := reflect.TypeOf(structure)
+    vt := reflect.ValueOf(structure)
+    monolog.Info("PutStruct: type %v value %v\n", st, vt)
+    
+    for i:= 0 ; i < st.NumField() ; i++ {
+        field := st.Field(i)
+        key := strings.ToLower(field.Name)
+        value :=  vt.Field(i).String()
+        me.Put(prefix + key, value)
+    }
+}
+
 
 type Error struct {
     error   string

+ 12 - 2
src/woe/world/account.go

@@ -73,6 +73,12 @@ func SitefStoreArray(rec sitef.Record, key string, val LabeledList) {
 }
 
 
+// Add a character to an account.
+func (me * Account) AddCharacter(chara * Character) {
+    me.characters = append(me.characters, chara)
+}
+
+
 // Save an account as a sitef file.
 func (me * Account) Save(dirname string) (err error) {
     path := SavePathFor(dirname, "account", me.Name)
@@ -88,6 +94,7 @@ func (me * Account) Save(dirname string) (err error) {
     for i, chara   := range me.characters {
         key        := fmt.Sprintf("characters[%d]", i)
         rec.Put(key, chara.Name)
+        
     }
     monolog.Debug("Saving Acccount record: %s %v", path, rec)
     return sitef.SaveRecord(path, rec)
@@ -121,13 +128,16 @@ func LoadAccount(dirname string, name string) (account *Account, err error) {
     
     var nchars int
     nchars                  = record.GetIntDefault("characters", 0)
-    _ = nchars
+    account.characters      = make([] * Character, nchars)
     /* Todo: load characters here... */    
-    monolog.Info("Loaded Account: %s %v", path, record)
+    monolog.Info("Loaded Account: %s %v", path, account)
     return account, nil
 }
 
  
+func (me * Account) NumCharacters() int {
+    return len(me.characters)
+} 
 
 func (me * Account) SaveXML(dirname string) (err error) {
     path := SavePathForXML(dirname, "account", me.Name)

File diff suppressed because it is too large
+ 541 - 114
src/woe/world/being.go


+ 29 - 11
src/woe/world/character.go

@@ -10,26 +10,44 @@ import "github.com/beoran/woe/sitef"
 
 type Character struct {
     Being       
-    AccountName string
-    account   * Account
+    * Account
 }
 
 
-func NewCharacter(being Being, accountname string, account * Account) (*Character) {
-    return &Character{being, accountname, account}
+func NewCharacterFromBeing(being Being, account * Account) (*Character) {
+    return &Character{being, account}
 }
 
+func (me * Character) Init(account * Account, name string, 
+              kin Entitylike, gender Entitylike, job Entitylike) (* Character) {
+    me.Account = account
+    me.Being.Init("character", name, account.Privilege, kin, gender, job)
+    return me
+}
+
+func NewCharacter(account * Account, name string, 
+              kin Entitylike, gender Entitylike, job Entitylike) (* Character) {
+    me := &Character{};
+    return me.Init(account, name, kin, gender, job)
+}
 
 // Save a character as a sitef record.
 func (me * Character) SaveSitef(rec sitef.Record) (err error) {
-    rec["accountname"]  = me.AccountName
-    // TODO: saving: me.Being.SaveSitef(rec)
+    rec["accountname"]  = me.Account.Name
+    me.Being.SaveSitef(rec)
+    
     return nil
 }
 
 // Load a character from a sitef record.
 func (me * Character) LoadSitef(rec sitef.Record) (err error) {
-    me.AccountName = rec["accountname"] 
+    aname := rec["accountname"]
+    account, err := DefaultWorld.LoadAccount(aname)
+    if err != nil {
+        return err
+    } 
+    me.Account = account
+    me.Being.LoadSitef(rec)
     // TODO: load being. me.Being.SaveSitef(rec)
     return nil
 }
@@ -63,18 +81,18 @@ func LoadCharacter(dirname string, name string) (character *Character, err error
     monolog.Info("Loading Account record: %s %v", path, record)
     
     character               = new(Character)
-    character.AccountName   = record["AccountName"]
-    account, err           := DefaultWorld.LoadAccount(dirname, character.AccountName);
+    aname                  := record["AccountName"]
+    account, err           := DefaultWorld.LoadAccount(aname);
     if err != nil  {
         return nil, err
     } 
     
     if account == nil {
         return nil, fmt.Errorf("Cound not load account %s for character %s", 
-            character.AccountName, character.Name)
+            aname, character.Name)
     }
     
-    character.account = account
+    character.Account = account
     
     return character, nil
 }

+ 161 - 7
src/woe/world/entity.go

@@ -1,6 +1,9 @@
 package world
+import "strings"
 import "os"
+import "sort"
 import "encoding/xml"
+import "github.com/beoran/woe/sitef"
 
 
 
@@ -18,18 +21,152 @@ type Typed interface {
 
 // An entity is anything that can exist in a World
 type Entity struct {
-    ID                  ID          `xml:"id,attr"`
+    ID                  string      `xml:"id,attr"`
     Name                string      `xml:"name,attr"`
     Short               string      `xml:"short,attr"`
     Long                string
-    Aliases           []string          
+    Aliases           []string    
+    // Privilege level needed to use/interact with/enter/... this Entity
+    Privilege           Privilege
 }
 
-func (me * Entity) Label() string {
+
+func (me * Entity) InitKind(kind string, name string, 
+    privilege Privilege) (* Entity) {
+    if me == nil {
+        return me
+    }
+    me.ID       = kind + "_" + strings.ToLower(name) 
+    me.Name     = name
+    me.Short    = name
+    me.Long     = name
+    me.Privilege= privilege
+    return me
+}
+
+// Devious polymorphism for Entities...
+type Entitylike interface {
+    AsEntity() * Entity
+}
+
+// A little trick to need less setters and getters
+func (me * Entity) AsEntity() (*Entity) {
+    return me
+}
+
+func (me Entity) Label() string {
     return string(me.ID)
 }
 
 
+type EntitySlice []Entitylike
+type EntityMap map[string]Entitylike
+
+type EntityLookup struct {
+    slice EntitySlice
+    table EntityMap  
+}
+
+
+// By is the type of a "less" function that defines the ordering of its Planet arguments.
+type EntityLikeBy func(p1, p2 Entitylike) bool
+
+// planetSorter joins a By function and a slice of Planets to be sorted.
+type EntityLikeSorter struct {
+    Entities EntitySlice
+    By       EntityLikeBy
+}
+
+
+func (me EntitySlice) By(compare func (p1, p2 Entitylike) bool) *EntityLikeSorter {
+    return &EntityLikeSorter { me,  EntityLikeBy(compare) };
+}
+
+// Len is part of sort.Interface.
+func (s * EntityLikeSorter) Len() int {
+    return len(s.Entities)
+}
+
+// Swap is part of sort.Interface.
+func (s *EntityLikeSorter) Swap(i, j int) {
+    s.Entities[i], s.Entities[j] = s.Entities[j], s.Entities[i]
+}
+
+// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter.
+func (s *EntityLikeSorter) Less(i, j int) bool {
+    return s.By(s.Entities[i], s.Entities[j])
+}
+
+func (me * EntityLikeSorter) Sort() {
+    sort.Sort(me)
+} 
+
+// Sort is a method on the function type, By, that sorts the argument slice according to the function.
+func (by EntityLikeBy) Sort(entities EntitySlice) {
+    ps := &EntityLikeSorter{
+        Entities: entities,
+        By:      by, // The Sort method's receiver is the function (closure) that defines the sort order.
+    }
+    sort.Sort(ps)
+}
+
+
+func (me EntityLookup) Get(index int) Entitylike {
+    return me.slice[index]
+}
+
+func (me EntityLookup) Len() int {
+    return len(me.table)
+}
+
+func (me * EntityLookup) Add(ent Entitylike) int {
+    me.slice = append(me.slice, ent)
+    me.slice.By(func (e1, e2 Entitylike) bool {
+            return (e1.AsEntity().Label() < e2.AsEntity().Label()) 
+        }).Sort()
+    res := len(me.slice) - 1 
+    if me.table == nil {
+        me.table = make(EntityMap)
+    }
+    me.table[ent.AsEntity().Label()] = ent
+    return res 
+}
+
+func (me EntityLookup) Lookup(id string) (Entitylike) {
+    res, ok := me.table[id]
+    if !ok {
+        return nil
+    }
+    return res    
+}
+
+func (me EntityLookup) Remove(ent Entitylike) bool {
+    key := ent.AsEntity().Label()
+    delete(me.table, key)
+    return true
+}
+
+type EntityLookupEachFunc func (id string, ent Entitylike, args...interface{}) Entitylike
+
+func (me EntityLookup) Each(lambda EntityLookupEachFunc, args...interface{}) Entitylike {
+    for k, v := range (me.table) {
+        res := lambda(k, v, args...) 
+        if res != nil {
+            return res
+        }
+    } 
+    return nil
+}
+
+
+type EntityList interface {
+    Get(int) Entitylike
+    Len() int
+    Add(Entitylike) int
+    Lookup(string) (Entitylike)
+    Remove(Entitylike) bool
+}
+
 // Interface 
 type Savable interface {
     Labeled
@@ -67,7 +204,7 @@ func LoadLoadable(dirname string, nameid string, result Loadable) (Loadable) {
 
 // A list of Identifier items mapped to their ID's 
 type LabeledLister interface {
-    Get(ID)         Labeled
+    Get(string)     Labeled
     Put(Labeled)
     Size()          int
     Index(int)      Labeled
@@ -76,15 +213,15 @@ type LabeledLister interface {
 
 type LabeledList struct {
     byList        []Labeled
-    byLabel       map[ID] Labeled
+    byLabel       map[string] Labeled
 }
 
 func NewLabeledList() * LabeledList {
-    byname := make(map[ID] Labeled)
+    byname := make(map[string] Labeled)
     return &LabeledList{nil, byname}
 }
 
-func (me * LabeledList) Get(id ID) Labeled {
+func (me * LabeledList) Get(id string) Labeled {
     val, ok := me.byLabel[id]
     if !ok { return nil }
     return val
@@ -98,6 +235,23 @@ func (me * LabeledList) Index(index int) Labeled {
 }
 
 
+// Save an entity to a sitef record.
+func (me * Entity) SaveSitef(rec sitef.Record) (err error) {
+    rec["id"]    = string(me.ID)
+    rec["name"]  = me.Name
+    rec["short"] = me.Short
+    rec["long"]  = me.Long
+    return nil
+}
+
+// Load an entity from a sitef record.
+func (me * Entity) LoadSitef(rec sitef.Record) (err error) {
+    me.ID       = rec["id"]   
+    me.Name     = rec["name"]
+    me.Short    = rec["short"]
+    me.Long     = rec["long"]
+    return nil
+}
 
 
 

+ 2 - 2
src/woe/world/inventory.go

@@ -3,8 +3,8 @@ package world
 
 
 type Inventory struct {
-    ItemIDS []int
-    items   []Item   
+    items   []*Item
+    Equipment map[string] string   
 }
 
 

+ 83 - 5
src/woe/world/item.go

@@ -1,5 +1,10 @@
 package world
 
+import "github.com/beoran/woe/sitef"
+import "github.com/beoran/woe/monolog"
+import "fmt"
+import "errors"
+
 type DamageKind string
 const (
     DAMAGE_CUT          DamageKind = "cut"
@@ -156,10 +161,18 @@ const (
     EQUIP_ARMS      EquipWhere = "arms"
     EQUIP_RIGHTRING EquipWhere = "rightring"
     EQUIP_LEFTRING  EquipWhere = "leftring"
+    EQUIP_BELT      EquipWhere = "belt"
     EQUIP_LIGHT     EquipWhere = "light"
     EQUIP_          EquipWhere = ""    
 )
 
+var EquipWhereList []EquipWhere = []EquipWhere {
+    EQUIP_HEAD,         EQUIP_TORSO,    EQUIP_OFFHAND,  EQUIP_DOMINANT,
+    EQUIP_AMMO,         EQUIP_FEET,     EQUIP_FOCUS,    EQUIP_PHONE,
+    EQUIP_GLOVES,       EQUIP_NECK,     EQUIP_LEGS,     EQUIP_ARMS,
+    EQUIP_RIGHTRING,    EQUIP_LEFTRING, EQUIP_BELT,     EQUIP_LIGHT,
+}
+
 type Item struct {
     Entity
     Quality       int
@@ -173,24 +186,89 @@ type Item struct {
     // be crafted nor harvested, nor mined.    
     Level         int
     // Id's of ingredients to craft this item. Empty if it cannot be crafted.
-    Ingredients []ID
+    Ingredients []string
     // Id of item this item can be upgraded/enhanced to. empty or "none"
     // if it cannot be upgraded.
-    Upgrade       ID
+    Upgrade       string
     // ID of item this item can degrade into. empty or "none" if cannot be 
     // degraded.
-    Degrade       ID
+    Degrade       string
     // ID of technique/art/item to craft this item teaches when used, empty or 
     // none if it teaches nothing. If it's a skill, the XP of teaching is 
     // determined by the Quality of the item.   
-    Teaches       ID
+    Teaches       string
+}
+
+// Load an item from a sitef file.
+func LoadItem(dirname string, id string) (item *Item, err error) {
+    
+    path := SavePathFor(dirname, "item", id)
+    
+    records, err := sitef.ParseFilename(path)
+    if err != nil {
+        return nil, err
+    }
+    
+    if len(records) < 1 {
+        return nil, errors.New("No item found!")
+    }
+    
+    record := records[0]
+    monolog.Info("Loading Item record: %s %v", path, record)
+    
+    item = new(Item)
+    item.Entity.LoadSitef(record)
+    /*
+    account.Name            = record.Get("name")
+    account.Hash            = record.Get("hash")
+    account.Algo            = record.Get("algo")
+    account.Email           = record.Get("email")
+    account.Points          = record.GetIntDefault("points", 0)
+    account.Privilege       = Privilege(record.GetIntDefault("privilege", 
+                                int(PRIVILEGE_NORMAL)))
+    
+    var nchars int
+    nchars                  = record.GetIntDefault("characters", 0)
+    _ = nchars    
+    */
+    monolog.Info("Loaded Item: %s %v", path, item)
+    return item, nil
 }
 
+
+
 type ItemPointer struct {
-    ID     ID
+    ID     string
     item * Item
 }
 
+type Equipment struct {
+    Equipped map[EquipWhere] * Item
+}
 
 
+func (me * Equipment) SaveSitef(rec sitef.Record) (err error) {
+    for k, v := range(me.Equipped) {
+        if v != nil {
+            key := fmt.Sprintf("equipment[%s]", k)
+            rec.Put(key, v.ID)
+        }
+    }
+    return nil
+}
+
+func (me * Equipment) LoadSitef(rec sitef.Record, world *World, dirname string) (err error) {
+    for k := range(EquipWhereList) {
+        key := fmt.Sprintf("equipment[%s]", k)
+        val, ok := rec.MayGet(key)
+        if ok {   
+            item, err := world.LoadItem(val)
+            if item != nil && err == nil {
+               me.Equipped[EquipWhere(k)] = item
+            }               
+        }
+    }
+    return nil
+}
+
 

+ 13 - 13
src/woe/world/skill.go

@@ -1,22 +1,22 @@
 package world
 
 const (
-    TALENT_NONE ID = "TALENT_NONE"
-    TALENT_STR  ID = "STR"
-    TALENT_TOU  ID = "TOU"
-    TALENT_AGI  ID = "AGI"
-    TALENT_DEX  ID = "DEX"
-    TALENT_INT  ID = "INT"
-    TALENT_WIS  ID = "WIS"
-    TALENT_CHA  ID = "CHA"
-    TALENT_EMO  ID = "EMO"
+    TALENT_NONE string = "TALENT_NONE"
+    TALENT_STR  string = "STR"
+    TALENT_TOU  string = "TOU"
+    TALENT_AGI  string = "AGI"
+    TALENT_DEX  string = "DEX"
+    TALENT_INT  string = "INT"
+    TALENT_WIS  string = "WIS"
+    TALENT_CHA  string = "CHA"
+    TALENT_EMO  string = "EMO"
 )
     
 
 type Skill struct {
     Entity
-    Kind              ID
-    Talent            ID
+    Kind              string
+    Talent            string
     derived           func (m * Being)  int
 }
 
@@ -85,7 +85,7 @@ var SkillList = []Skill {
       Short: "Martial arts and unarmed fighting, use of gloves.", },
       Talent : TALENT_TOU,
     },
-    { Entity : Entity { ID: "skill_mauls", Name: "Mauls", 
+    { Entity : Entity { ID: "skill_maul", Name: "Maul", 
       Short: "Fighting with heavy meelee weapons such as axes and hammers", },
       Talent : TALENT_TOU,
     },
@@ -156,7 +156,7 @@ var SkillList = []Skill {
       Talent : TALENT_AGI,
     },
     { Entity : Entity { ID: "skill_traveling", Name: "Traveling", 
-      Short: "Efficient traveling and skills to reduce MP efficiency", },
+      Short: "Efficient traveling and skills to reduce MP use.", },
       Talent : TALENT_AGI,
     },
  

+ 7 - 7
src/woe/world/technique.go

@@ -10,11 +10,11 @@ package world
  
 type Technique struct {
     Entity
-    Kind        ID
-    Effect      ID
+    Kind        string
+    Effect      string
     Level       int
     Cost        int
-    Skill       ID
+    Skill       string
     skill     * Skill
     onUse       func (me * Technique, caster * Being, targets ...*Being) (bool)
 }
@@ -23,8 +23,8 @@ type Technique struct {
 type BeingTechnique struct {
     being       * Being
     technique   * Technique
-    Being         ID
-    Technique     ID
+    Being         string
+    Technique     string
 }
 
 /* An exploit is a special technique that can only be used a few times 
@@ -41,8 +41,8 @@ type Exploit    Technique
 type BeingExploit struct {
     being       * Being
     exploit     * Exploit
-    Being         ID
-    Exploit       ID
+    Being         string
+    Exploit       string
     // How many times the exploit may be used.
     Uses          Vital
 }

+ 55 - 18
src/woe/world/world.go

@@ -11,23 +11,21 @@ import "errors"
  * is kept statically delared in code for simplicity.
 */
 
-/* ID used for anything in a world but the world itself and the account. */
-type ID string 
-
 
 type World struct {
     Name                      string
     MOTD                      string
-    entitymap       map[ID] * Entity
-    zonemap         map[ID] * Zone
+    dirname                   string
+    entitymap       map[string] * Entity
+    zonemap         map[string] * Zone
     zones                [] * Zone
-    charactermap    map[ID] * Character
+    charactermap    map[string] * Character
     characters           []   Character
-    roommap         map[ID] * Room
+    roommap         map[string] * Room
     rooms                []   Room
-    itemmap         map[ID] * Item
+    itemmap         map[string] * Item
     items                []   Item
-    mobilemap       map[ID] * Mobile
+    mobilemap       map[string] * Mobile
     mobiles              []   Mobile
     accounts             [] * Account
     accountmap      map[string] * Account
@@ -45,18 +43,22 @@ func (me * World) AddWoeDefaults() {
     */
 }
 
-func NewWorld(name string, motd string) (*World) {
+func NewWorld(name string, motd string, dirname string) (*World) {
     world := new(World)
-    world.Name = name
-    world.MOTD = motd
-    world.accountmap = make(map[string] * Account)
-    
+    world.Name          = name
+    world.MOTD          = motd
+    world.dirname       = dirname
+    world.accountmap    = make(map[string] * Account)
+    world.itemmap       = make(map[string] * Item)
+    world.roommap       = make(map[string] * Room)
+    world.charactermap  = make(map[string] * Character)
+
     world.AddWoeDefaults()
     return world;
 }
 
 
-func HaveID(ids [] ID, id ID) bool {
+func HaveID(ids [] string, id string) bool {
     for index := 0 ; index < len(ids) ; index++ {
         if ids[index] == id { return true }  
     }
@@ -101,7 +103,7 @@ func LoadWorld(dirname string, name string) (world * World, err error) {
     record := records[0]
     monolog.Info("Loading World record: %s %v", path, record)
     
-    world = NewWorld(record.Get("name"), record.Get("motd"))
+    world = NewWorld(record.Get("name"), record.Get("motd"), dirname)
     monolog.Info("Loaded World: %s %v", path, world)
     return world, nil
 }
@@ -118,13 +120,13 @@ func (me * World) GetAccount(name string) (account * Account) {
 
 // Loads an account to be used with this world. Characters will be linked.
 // If the account was already loaded, returns that in stead.
-func (me * World) LoadAccount(dirname string, name string) (account *Account, err error) {
+func (me * World) LoadAccount(name string) (account *Account, err error) {
     account = me.GetAccount(name)
     if (account != nil) {
         return account, nil
     }
     
-    account, err = LoadAccount(dirname, name);
+    account, err = LoadAccount(me.dirname, name);
     if err != nil {
         return account, err
     }
@@ -145,3 +147,38 @@ func (me * World) RemoveAccount(name string) {
 var DefaultWorld * World
 
 
+// Returns an item that has already been loaded or nil if not found
+func (me * World) GetItem(id string) (item * Item) {
+    item, ok := me.itemmap[id]
+    if !ok {
+        return nil
+    }
+    return item
+} 
+
+// Loads an item to be used with this world. 
+// If the item was already loaded, returns that in stead.
+func (me * World) LoadItem(id string) (item *Item, err error) {
+    item = me.GetItem(id)
+    
+    if (item != nil) {
+        return item, nil
+    }
+    
+    item, err = LoadItem(me.dirname, id);
+    if err != nil {
+        return item, err
+    }
+    me.itemmap[item.ID] = item
+    return item, nil
+}
+
+// Removes an item from this world by ID.
+func (me * World) RemoveItem(id string) {
+    _, have := me.itemmap[id]
+    if (!have) {
+        return
+    }    
+    delete(me.itemmap, id)
+}
+

Some files were not shown because too many files changed in this diff