예제 #1
0
파일: door.go 프로젝트: BenLubar/Rnoadm
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)
	}
}
예제 #2
0
파일: rock.go 프로젝트: BenLubar/Rnoadm
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)
					},
				},
			},
		})
	}
}
예제 #3
0
파일: tree.go 프로젝트: BenLubar/Rnoadm
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)
					},
				},
			},
		})
	}
}
예제 #4
0
파일: forge.go 프로젝트: BenLubar/Rnoadm
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,
		})
	}
}
예제 #5
0
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
			}
		}
	}
}