func (d *Door) Interact(player world.PlayerLike, action string) { switch action { case "open": pos := d.Position() if pos == nil { return } x, y := pos.Position() px, py := player.Position().Position() if (px == x && py != y-1 && py != y+1) || (py == y && px != x-1 && px != x+1) || (px != x && py != y) { player.SetSchedule(&world.ScheduleSchedule{ Schedules: []world.Schedule{ world.NewWalkSchedule(x, y, true, uint(player.Weight()/player.WeightMax())), &world.ActionSchedule{ Action: "open", Target_: d.Outer().(world.Visible), }, }, }) return } d.mtx.Lock() d.open = true d.mtx.Unlock() pos.Zone().Update(pos, d) case "close": pos := d.Position() if pos == nil { return } x, y := pos.Position() px, py := player.Position().Position() if (px == x && (py < y-1 || py > y+1)) || (py == y && (px < x-1 || px > x+1)) || (px != x && py != y) { player.SetSchedule(&world.ScheduleSchedule{ Schedules: []world.Schedule{ world.NewWalkSchedule(x, y, true, uint(player.Weight()/player.WeightMax())), &world.ActionSchedule{ Action: "close", Target_: d.Outer().(world.Visible), }, }, }) return } d.mtx.Lock() d.open = false d.mtx.Unlock() pos.Zone().Update(pos, d) default: d.VisibleObject.Interact(player, action) } }
func (r *Rock) Interact(player world.PlayerLike, action string) { switch action { default: r.Node.Interact(player, action) case "quarry": pos := r.Position() if pos == nil { return } x, y := pos.Position() player.SetSchedule(&world.ScheduleSchedule{ Schedules: []world.Schedule{ world.NewWalkSchedule(x, y, true, 0), &GatherSchedule{ Tool: player, Target_: r, Item: func(volume uint64) world.Visible { return world.InitObject(&Stone{ material: r.material.CopyStone(volume), }).(world.Visible) }, }, }, }) case "mine": if r.material.MetalColor() == "" { return } pos := r.Position() if pos == nil { return } x, y := pos.Position() player.SetSchedule(&world.ScheduleSchedule{ Schedules: []world.Schedule{ world.NewWalkSchedule(x, y, true, 0), &GatherSchedule{ Tool: player, Target_: r, Item: func(volume uint64) world.Visible { return world.InitObject(&Ore{ material: r.material.CopyMetal(volume), }).(world.Visible) }, }, }, }) } }
func (t *Tree) Interact(player world.PlayerLike, action string) { switch action { default: t.Node.Interact(player, action) case "chop": pos := t.Position() if pos == nil { return } x, y := pos.Position() player.SetSchedule(&world.ScheduleSchedule{ Schedules: []world.Schedule{ world.NewWalkSchedule(x, y, true, 0), &GatherSchedule{ Tool: player, Target_: t, Item: func(volume uint64) world.Visible { return world.InitObject(&Logs{ material: t.material.CopyWood(volume), }).(world.Visible) }, }, }, }) } }
func (f *Forge) Interact(player world.PlayerLike, action string) { switch action { default: f.VisibleObject.Interact(player, action) case "smelt": pos := f.Position() ppos := player.Position() if pos == nil || ppos == nil { return } x, y := pos.Position() if px, py := ppos.Position(); px != x || py != y+1 { player.SetSchedule(&world.ScheduleSchedule{ Schedules: []world.Schedule{ world.NewWalkSchedule(x, y, false, uint(player.Weight()/player.WeightMax())), &world.ActionSchedule{ Target_: f, Action: action, }, }, }) return } ores := []interface{}{} for _, v := range player.Inventory() { if o, ok := v.(*Ore); ok { ores = append(ores, map[string]interface{}{ "i": o.NetworkID(), "q": o.Material().Quality(), "v": o.Volume(), "w": o.Weight(), }) } } for _, v := range player.Inventory() { if s, ok := v.(*Stone); ok { ores = append(ores, map[string]interface{}{ "i": s.NetworkID(), "q": s.Material().Quality(), "v": s.Volume(), "w": s.Weight(), }) } } instance := player.Instance(f.Position()) var done time.Time instance.Last(func(last time.Time) time.Time { done = last return last }) contents := []interface{}{} instance.Items(func(items []world.Visible) []world.Visible { for _, i := range items { var sprites []map[string]interface{} for j, c := range i.Colors() { sprites = append(sprites, map[string]interface{}{ "S": i.Sprite(), "C": c, "E": map[string]interface{}{ "y": j, }, }) } contents = append(contents, sprites) // 4 minutes per 5kg of ore done = done.Add(time.Minute * 4 * time.Duration(i.(world.Item).Weight()) / 5000) } if done.Before(time.Now()) && len(items) > 0 { materials := make(map[MetalType]*material) var quality big.Int var totalVolume uint64 var tmp big.Int for _, i := range items { if o, ok := i.(*Ore); ok { for _, m := range o.material.components { if m.metal == nil || m.volume == 0 { continue } mat := materials[*m.metal] if mat == nil { mat = &material{metal: m.metal} materials[*m.metal] = mat } totalVolume += m.volume mat.volume += m.volume tmp.SetUint64(m.volume) quality.Add(&quality, tmp.Mul(&o.material.quality, &tmp)) } } } originalVolume := totalVolume quality.Div(&quality, tmp.SetUint64(totalVolume)) makeIngot := func(volume uint64) { ingot := &Ingot{} world.InitObject(ingot) ingot.material = &Material{ components: make([]*material, 0, len((materials))), quality: quality, } world.InitObject(ingot.material) remaining := originalVolume for _, m := range materials { v := m.volume * volume / remaining remaining -= m.volume volume -= v ingot.material.components = append(ingot.material.components, &material{ metal: m.metal, volume: v, }) } ingot.material.sortComponents() if !player.GiveItem(ingot) { player.Position().Add(ingot) } } for totalVolume != 0 { if totalVolume <= 1000 { makeIngot(totalVolume) break } makeIngot(1000) totalVolume -= 1000 } items = items[:0] contents = contents[:0] } return items }) player.SetHUD("forge", map[string]interface{}{ "O": ores, "C": contents, "T": done, }) } }
func socketHandler(ws *websocket.Conn) { defer ws.Close() addr, _, err := net.SplitHostPort(ws.Request().RemoteAddr) if err != nil { panic(err) } packets := make(chan clientPacket) go func() { defer close(packets) for { var packet clientPacket if err := websocket.JSON.Receive(ws, &packet); err != nil { if err == io.EOF { return } if _, ok := err.(*net.OpError); ok { return } log.Printf("%s: %T %v", addr, err, err) return } packets <- packet } }() err = websocket.JSON.Send(ws, packetClientHash) if err != nil { if err != io.EOF { log.Printf("[err_sock] %s:%#v %v", addr, packetClientHash, err) } return } var ( player *hero.Player zone *world.Zone kick <-chan string hud <-chan hero.HUD inventory <-chan []world.Visible messages <-chan []hero.Message updateQueue packetUpdate ) updateTick := time.NewTicker(time.Second / 10) defer updateTick.Stop() updates := make(chan []packetUpdateUpdate, 1) floatingText := make(chan packetFloatingText, 32) sendUpdates := func(newUpdates ...packetUpdateUpdate) { for { select { case updates <- newUpdates: return case other := <-updates: newUpdates = append(other, newUpdates...) } } } var listener *world.ZoneListener updateWalls := func(t *world.Tile, obj world.ObjectLike) { if _, ok := obj.(material.IsWall); ok { x, y := t.Position() if x > 0 { tile := t.Zone().Tile(x-1, y) for _, w := range tile.Objects() { if _, ok := w.(material.IsWall); ok { listener.Update(tile, w) } } } if x < 255 { tile := t.Zone().Tile(x+1, y) for _, w := range tile.Objects() { if _, ok := w.(material.IsWall); ok { listener.Update(tile, w) } } } if y > 0 { tile := t.Zone().Tile(x, y-1) for _, w := range tile.Objects() { if _, ok := w.(material.IsWall); ok { listener.Update(tile, w) } } } if y < 255 { tile := t.Zone().Tile(x, y+1) for _, w := range tile.Objects() { if _, ok := w.(material.IsWall); ok { listener.Update(tile, w) } } } } } toObject := func(o world.Visible) *packetUpdateObject { var health *[2]uint64 if c, ok := o.(world.Combat); ok { h, m := c.Health(), c.MaxHealth() if l := m.BitLen(); l > 64 { h = (&big.Int{}).Rsh(h, uint(l-64)) m = (&big.Int{}).Rsh(m, uint(l-64)) } health = &[2]uint64{h.Uint64(), m.Uint64()} } return addSprites(&packetUpdateObject{ Name: o.Name(), Actions: o.Actions(player), Health: health, }, o) } listener = &world.ZoneListener{ Add: func(t *world.Tile, obj world.ObjectLike) { o, ok := obj.(world.Visible) if !ok { return } x, y := t.Position() sendUpdates(packetUpdateUpdate{ ID: o.NetworkID(), X: x, Y: y, Object: toObject(o), }) updateWalls(t, obj) }, Remove: func(t *world.Tile, obj world.ObjectLike) { o, ok := obj.(world.Visible) if !ok { return } x, y := t.Position() sendUpdates(packetUpdateUpdate{ ID: o.NetworkID(), X: x, Y: y, Remove: true, }) updateWalls(t, obj) }, Move: func(from, to *world.Tile, obj world.ObjectLike) { o, ok := obj.(world.Visible) if !ok { return } fx, fy := from.Position() tx, ty := to.Position() sendUpdates(packetUpdateUpdate{ ID: o.NetworkID(), X: tx, Y: ty, FromX: &fx, FromY: &fy, }) updateWalls(from, obj) updateWalls(to, obj) }, Update: func(t *world.Tile, obj world.ObjectLike) { o, ok := obj.(world.Visible) if !ok { return } x, y := t.Position() sendUpdates(packetUpdateUpdate{ ID: o.NetworkID(), X: x, Y: y, Object: toObject(o), }) }, Damage: func(attacker, victim world.Combat, amount *big.Int) { t := victim.Position() if t == nil { return } x, y := t.Position() sendUpdates(packetUpdateUpdate{ ID: victim.NetworkID(), X: x, Y: y, Object: toObject(victim), }) text := packetFloatingTextText{ X: x, Y: y, Color: "#fff", } if amount == world.DamageMissed { text.Text = "MISS" } else if amount == world.DamageBlocked { text.Text = "BLOCK" } else if amount == world.DamageResisted { text.Text = "RESIST" } else { text.Text = material.Comma(amount) } select { case floatingText <- packetFloatingText{text}: default: } }, } next: for { select { case packet, ok := <-packets: if !ok { return } if packet.Auth != nil { if player != nil { return } var fail string player, fail = hero.Login(addr, packet.Auth) if fail != "" { err = websocket.JSON.Send(ws, packetKick{fail}) if err != nil && err != io.EOF { log.Printf("[err_sock] %s:%#v %v", addr, packetKick{fail}, err) } return } world.InitObject(player) kick, hud, inventory, messages = player.InitPlayer() zx, tx, zy, ty, zz := player.LoginPosition() defer hero.PlayerDisconnected(player) zone = world.GetZone(zx, zy, zz) zone.AddListener(listener) if player.CanSpawn() { zone.Tile(tx, ty).Add(player) } else { player.CharacterCreation("") } defer func() { if t := player.Position(); t != nil { player.UpdatePosition() t.Remove(player) } zone.RemoveListener(listener) world.ReleaseZone(zone) }() inventoryObjects := []*packetInventoryItem{} for _, item := range player.Inventory() { inventoryObjects = append(inventoryObjects, &packetInventoryItem{ ID: item.NetworkID(), Object: addSprites(&packetUpdateObject{ Name: item.Name(), Actions: item.Actions(player), }, item), }) } err = websocket.JSON.Send(ws, packetInventory{inventoryObjects}) if err != nil { if err != io.EOF { log.Printf("[err_sock] %s:%#v %v", addr, packetInventory{inventoryObjects}, err) } return } } if player == nil { continue } if packet.Walk != nil { if t := player.Position(); t != nil { x, y := t.Position() if packet.Walk.X == x && packet.Walk.Y == y { player.ClearSchedule() } else { player.SetSchedule(world.NewWalkSchedule(packet.Walk.X, packet.Walk.Y, false, uint(player.Weight()/player.WeightMax()))) } } } if packet.HUD != nil { switch packet.HUD.Name { default: return case "cc": if cmd, ok := packet.HUD.Data.(string); ok { player.CharacterCreation(cmd) } else { return } case "forge": data, ok := packet.HUD.Data.(map[string]interface{}) if !ok { return } pos := player.Position() if pos == nil { return } x, y := pos.Position() if y == 0 { return } y-- t := pos.Zone().Tile(x, y) for _, o := range t.Objects() { if forge, ok := o.(*material.Forge); ok { forge.Command(player, data) continue next } } return } } if packet.Chat != nil { if strings.TrimSpace(*packet.Chat) != "" { player.Chat(addr, *packet.Chat) } } if packet.Mouse != nil { color := "#0f0" if player.Position() == nil || player.Position().Zone().Tile(packet.Mouse.X, packet.Mouse.Y).Blocked() { color = "#f00" } update := map[string][]map[string]interface{}{ "Update": { { "I": "_crosshair", "X": packet.Mouse.X, "Y": packet.Mouse.Y, "O": map[string][]packetUpdateSprite{ "S": { { Sheet: "ui_icons", Color: color, Extra: map[string]interface{}{ "x": 1, }, }, }, }, }, }, } if packet.Mouse.Fx != packet.Mouse.X || packet.Mouse.Fy != packet.Mouse.Y { update["Update"] = append([]map[string]interface{}{{ "I": "_crosshair", "R": true, "X": packet.Mouse.Fx, "Y": packet.Mouse.Fy, }}, update["Update"]...) } err = websocket.JSON.Send(ws, update) if err != nil { if err != io.EOF { log.Printf("[err_sock] %s:%#v %v", addr, update, err) } return } } if packet.Interact != nil { if packet.Interact.X < 8 { inv := player.Inventory() i := int(packet.Interact.X) + 8*int(packet.Interact.Y) if len(inv) > i { if inv[i].NetworkID() == packet.Interact.ID { inv[i].Interact(player, packet.Interact.Action) continue } } } if player.Position() == nil { continue } for _, obj := range player.Position().Zone().Tile(packet.Interact.X, packet.Interact.Y).Objects() { if v, ok := obj.(world.Visible); ok && v.NetworkID() == packet.Interact.ID { v.Interact(player, packet.Interact.Action) break } } } if len(packet.Admin) > 0 { player.AdminCommand(addr, packet.Admin...) } case message := <-kick: websocket.JSON.Send(ws, packetKick{message}) if err != nil { if err != io.EOF { log.Printf("[err_sock] %s:%#v %v", addr, packetKick{message}, err) } return } return case h := <-hud: err = websocket.JSON.Send(ws, packetHUD{h}) if err != nil { if err != io.EOF { log.Printf("[err_sock] %s:%#v %v", addr, packetHUD{h}, err) } return } case items := <-inventory: objects := make([]*packetInventoryItem, len(items)) for i, item := range items { objects[i] = &packetInventoryItem{ ID: item.NetworkID(), Object: addSprites(&packetUpdateObject{ Name: item.Name(), Actions: item.Actions(player), }, item), } } err = websocket.JSON.Send(ws, packetInventory{objects}) if err != nil { if err != io.EOF { log.Printf("[err_sock] %s:%#v %v", addr, packetInventory{objects}, err) } return } case update := <-updates: updateQueue.Update = append(updateQueue.Update, update...) case text := <-floatingText: err = websocket.JSON.Send(ws, text) if err != nil { if err != io.EOF { log.Printf("[err_sock] %s:%#v %v", addr, text, err) } return } case <-updateTick.C: if len(updateQueue.Update) == 0 { continue } if player == nil { continue } leftover := updateQueue.Update[:0] if len(updateQueue.Update) > 100 { updateQueue.Update, leftover = updateQueue.Update[:100], updateQueue.Update[100:] } if t := player.Position(); t != nil { updateQueue.PlayerX, updateQueue.PlayerY = t.Position() } else { updateQueue.PlayerX, updateQueue.PlayerY = 127, 127 } err = websocket.JSON.Send(ws, updateQueue) if err != nil { if err != io.EOF { log.Printf("[err_sock] %s:%#v %v", addr, updateQueue, err) } return } updateQueue.Update = leftover case msg := <-messages: err = websocket.JSON.Send(ws, packetMessage{msg}) if err != nil { if err != io.EOF { log.Printf("[err_sock] %s:%#v %v", addr, packetMessage{msg}, err) } return } } } }