From 6dae0097b9d302b4a6def9e72771124c14837b33 Mon Sep 17 00:00:00 2001 From: Tim Sarbin Date: Thu, 25 Jun 2020 00:39:09 -0400 Subject: [PATCH] Added npc labels. More mapgen stuff. (#449) --- d2core/d2map/d2mapentity/map_entity.go | 10 ++++ d2core/d2map/d2mapentity/npc.go | 25 ++++++++++ d2core/d2map/d2mapentity/player.go | 14 ++++-- d2core/d2map/d2mapgen/act1_overworld.go | 62 +++++++++++++++++++++++++ d2core/d2map/d2maprenderer/renderer.go | 8 ++++ d2core/d2map/d2maprenderer/viewport.go | 11 +++++ d2game/d2gamescreen/game.go | 3 ++ d2game/d2player/game_controls.go | 35 +++++++++++++- 8 files changed, 162 insertions(+), 6 deletions(-) diff --git a/d2core/d2map/d2mapentity/map_entity.go b/d2core/d2map/d2mapentity/map_entity.go index 83256a69..648b715a 100644 --- a/d2core/d2map/d2mapentity/map_entity.go +++ b/d2core/d2map/d2mapentity/map_entity.go @@ -12,6 +12,8 @@ type MapEntity interface { Render(target d2render.Surface) Advance(tickTime float64) GetPosition() (float64, float64) + GetPositionF() (float64, float64) + Name() string } // mapEntity represents an entity on the map that can be animated @@ -169,3 +171,11 @@ func angleToDirection(angle float64) int { func (m *mapEntity) GetPosition() (float64, float64) { return float64(m.TileX), float64(m.TileY) } + +func (m *mapEntity) GetPositionF() (float64, float64) { + return float64(m.TileX) + (float64(m.subcellX)/5.0), float64(m.TileY) + (float64(m.subcellY)/5.0) +} + +func (m *mapEntity) Name() string { + return "" +} diff --git a/d2core/d2map/d2mapentity/npc.go b/d2core/d2map/d2mapentity/npc.go index ed5c68a7..156d268d 100644 --- a/d2core/d2map/d2mapentity/npc.go +++ b/d2core/d2map/d2mapentity/npc.go @@ -22,6 +22,7 @@ type NPC struct { repetitions int direction int objectLookup *d2datadict.ObjectLookupRecord + name string } func CreateNPC(x, y int, object *d2datadict.ObjectLookupRecord, direction int) *NPC { @@ -39,6 +40,26 @@ func CreateNPC(x, y int, object *d2datadict.ObjectLookupRecord, direction int) * result.SetMode(object.Mode, object.Class, direction) result.mapEntity.directioner = result.rotate + switch object.Act { + case 1: + switch object.Id { + case 0: + result.name = "Gheed" + case 1: + result.name = "Cain" + case 2: + result.name = "Akara" + case 5: + result.name = "Kashya" + case 7: + result.name = "Warriv" + case 8: + result.name = "Charsi" + case 9: + result.name = "Andariel" + } + } + return result } @@ -143,3 +164,7 @@ func (v *NPC) SetMode(animationMode, weaponClass string, direction int) error { return err } + +func (m *NPC) Name() string { + return m.name +} diff --git a/d2core/d2map/d2mapentity/player.go b/d2core/d2map/d2mapentity/player.go index c82aaa16..6295e7b5 100644 --- a/d2core/d2map/d2mapentity/player.go +++ b/d2core/d2map/d2mapentity/player.go @@ -18,7 +18,7 @@ type Player struct { Equipment d2inventory.CharacterEquipment Id string direction int - Name string + name string nameLabel d2ui.Label lastPathSize int isInTown bool @@ -59,7 +59,7 @@ func CreatePlayer(id, name string, x, y int, direction int, heroType d2enum.Hero composite: composite, Equipment: equipment, direction: direction, - Name: name, + name: name, nameLabel: d2ui.CreateLabel(d2resource.FontFormal11, d2resource.PaletteStatic), isRunToggled: true, isInTown: true, @@ -126,9 +126,9 @@ func (v *Player) Render(target d2render.Surface) { ) defer target.Pop() v.composite.Render(target) - v.nameLabel.X = v.offsetX - v.nameLabel.Y = v.offsetY - 100 - v.nameLabel.Render(target) + //v.nameLabel.X = v.offsetX + //v.nameLabel.Y = v.offsetY - 100 + //v.nameLabel.Render(target) } func (v *Player) SetMode(animationMode, weaponClass string, direction int) error { @@ -177,3 +177,7 @@ func (v *Player) rotate(direction int) { v.SetMode(newAnimationMode, v.weaponClass, direction) } } + +func (v *Player) Name() string { + return v.name +} diff --git a/d2core/d2map/d2mapgen/act1_overworld.go b/d2core/d2map/d2mapgen/act1_overworld.go index bfbd4499..42c04833 100644 --- a/d2core/d2map/d2mapgen/act1_overworld.go +++ b/d2core/d2map/d2mapgen/act1_overworld.go @@ -267,6 +267,68 @@ func generateWilderness1Contents(mapEngine *d2mapengine.MapEngine, rect d2common } } + stuff := []*d2mapstamp.Stamp{ + loadPreset(mapEngine, d2wilderness.StoneFill1, 0), + loadPreset(mapEngine, d2wilderness.StoneFill1, 1), + loadPreset(mapEngine, d2wilderness.StoneFill1, 2), + loadPreset(mapEngine, d2wilderness.StoneFill2, 0), + loadPreset(mapEngine, d2wilderness.StoneFill2, 1), + loadPreset(mapEngine, d2wilderness.StoneFill2, 2), + loadPreset(mapEngine, d2wilderness.Cottages1, 0), + loadPreset(mapEngine, d2wilderness.Cottages1, 1), + loadPreset(mapEngine, d2wilderness.Cottages1, 2), + loadPreset(mapEngine, d2wilderness.Cottages1, 3), + loadPreset(mapEngine, d2wilderness.Cottages1, 4), + loadPreset(mapEngine, d2wilderness.Cottages1, 5), + loadPreset(mapEngine, d2wilderness.FallenCamp1, 0), + loadPreset(mapEngine, d2wilderness.FallenCamp1, 1), + loadPreset(mapEngine, d2wilderness.FallenCamp1, 2), + loadPreset(mapEngine, d2wilderness.FallenCamp1, 3), + loadPreset(mapEngine, d2wilderness.Pond, 0), + loadPreset(mapEngine, d2wilderness.SwampFill1, 0), + loadPreset(mapEngine, d2wilderness.SwampFill2, 0), + } + mapEngine.PlaceStamp(denOfEvil, denOfEvilLoc.X, denOfEvilLoc.Y) + numPlaced := 0 + for numPlaced < 25 { + stamp := stuff[rand.Intn(len(stuff))] + + stampRect := d2common.Rectangle { + Left: rect.Left+ rand.Intn(rect.Width) - stamp.Size().Width, + Top: rect.Top+rand.Intn(rect.Height) - stamp.Size().Height, + Width: stamp.Size().Width, + Height: stamp.Size().Height, + } + + if areaEmpty(mapEngine, stampRect) { + mapEngine.PlaceStamp(stamp, stampRect.Left, stampRect.Top) + numPlaced++ + } + } + +} + +func areaEmpty(mapEngine *d2mapengine.MapEngine, rect d2common.Rectangle) bool { + mapHeight := mapEngine.Size().Height + mapWidth := mapEngine.Size().Width + + if rect.Bottom() >= mapHeight || rect.Right() >= mapWidth { + return false + } + + for y := rect.Top; y <= rect.Bottom(); y++ { + for x := rect.Left; x <= rect.Right(); x++ { + if len(mapEngine.Tile(x, y).Floors) == 0 { + continue + } + floor := mapEngine.Tile(x, y).Floors[0] + if floor.Style != 0 || floor.Sequence != 0 || floor.Prop1 != 1 { + return false + } + } + } + + return true } diff --git a/d2core/d2map/d2maprenderer/renderer.go b/d2core/d2map/d2maprenderer/renderer.go index cd7e1960..4a9e06dc 100644 --- a/d2core/d2map/d2maprenderer/renderer.go +++ b/d2core/d2map/d2maprenderer/renderer.go @@ -240,6 +240,14 @@ func (mr *MapRenderer) renderDebug(debugVisLevel int, target d2render.Surface, s } } +func (mr *MapRenderer) WorldToScreen(x, y float64) (int, int) { + return mr.viewport.WorldToScreen(x, y) +} + +func (mr *MapRenderer) WorldToScreenF(x, y float64) (float64, float64) { + return mr.viewport.WorldToScreenF(x, y) +} + func (mr *MapRenderer) renderTileDebug(ax, ay int, debugVisLevel int, target d2render.Surface) { subTileColor := color.RGBA{R: 80, G: 80, B: 255, A: 50} tileColor := color.RGBA{R: 255, G: 255, B: 255, A: 100} diff --git a/d2core/d2map/d2maprenderer/viewport.go b/d2core/d2map/d2maprenderer/viewport.go index 8bf8f212..f8807a99 100644 --- a/d2core/d2map/d2maprenderer/viewport.go +++ b/d2core/d2map/d2maprenderer/viewport.go @@ -51,6 +51,10 @@ func (v *Viewport) WorldToScreen(x, y float64) (int, int) { return v.OrthoToScreen(v.WorldToOrtho(x, y)) } +func (v *Viewport) WorldToScreenF(x, y float64) (float64, float64) { + return v.OrthoToScreenF(v.WorldToOrtho(x, y)) +} + func (v *Viewport) ScreenToWorld(x, y int) (float64, float64) { return v.OrthoToWorld(v.ScreenToOrtho(x, y)) } @@ -81,6 +85,13 @@ func (v *Viewport) OrthoToScreen(x, y float64) (int, int) { return orthoX, orthoY } +func (v *Viewport) OrthoToScreenF(x, y float64) (float64, float64) { + camOrthoX, camOrthoY := v.getCameraOffset() + orthoX := x - camOrthoX + float64(v.screenRect.Left) + orthoY := y - camOrthoY + float64(v.screenRect.Top) + return orthoX, orthoY +} + func (v *Viewport) IsTileVisible(x, y float64) bool { orthoX1, orthoY1 := v.WorldToOrtho(x-3, y) orthoX2, orthoY2 := v.WorldToOrtho(x+3, y) diff --git a/d2game/d2gamescreen/game.go b/d2game/d2gamescreen/game.go index 2e4687f1..fa00035d 100644 --- a/d2game/d2gamescreen/game.go +++ b/d2game/d2gamescreen/game.go @@ -56,11 +56,14 @@ func (v *Game) Render(screen d2render.Surface) error { v.gameClient.RegenMap = false v.mapRenderer.RegenerateTileCache() } + screen.Clear(color.Black) v.mapRenderer.Render(screen) + if v.gameControls != nil { v.gameControls.Render(screen) } + return nil } diff --git a/d2game/d2player/game_controls.go b/d2game/d2player/game_controls.go index 5df30b25..297c130c 100644 --- a/d2game/d2player/game_controls.go +++ b/d2game/d2player/game_controls.go @@ -3,6 +3,7 @@ package d2player import ( "image/color" "log" + "math" "time" "github.com/OpenDiablo2/OpenDiablo2/d2common" @@ -37,6 +38,8 @@ type GameControls struct { escapeMenu *EscapeMenu inputListener InputCallbackListener FreeCam bool + lastMouseX int + lastMouseY int // UI globeSprite *d2ui.Sprite @@ -44,6 +47,7 @@ type GameControls struct { menuButton *d2ui.Sprite skillIcon *d2ui.Sprite zoneChangeText *d2ui.Label + nameLabel *d2ui.Label runButton d2ui.Button isZoneTextShown bool actionableRegions []ActionableRegion @@ -76,6 +80,11 @@ func NewGameControls(hero *d2mapentity.Player, mapEngine *d2mapengine.MapEngine, label.Color = color.RGBA{R: 255, G: 88, B: 82, A: 255} label.Alignment = d2ui.LabelAlignCenter + nameLabel := d2ui.CreateLabel(d2resource.FontFormal11, d2resource.PaletteStatic) + nameLabel.Alignment = d2ui.LabelAlignCenter + nameLabel.SetText("") + nameLabel.Color = color.White + gc := &GameControls{ hero: hero, mapEngine: mapEngine, @@ -84,6 +93,7 @@ func NewGameControls(hero *d2mapentity.Player, mapEngine *d2mapengine.MapEngine, inventory: NewInventory(), heroStats: NewHeroStats(), escapeMenu: NewEscapeMenu(), + nameLabel: &nameLabel, zoneChangeText: &label, actionableRegions: []ActionableRegion{ {leftSkill, d2common.Rectangle{Left: 115, Top: 550, Width: 50, Height: 50}}, @@ -191,12 +201,15 @@ func (g *GameControls) OnMouseButtonRepeat(event d2input.MouseEvent) bool { } func (g *GameControls) OnMouseMove(event d2input.MouseMoveEvent) bool { + mx, my := event.X, event.Y + g.lastMouseX = mx + g.lastMouseY = my + if g.escapeMenu.IsOpen() { g.escapeMenu.OnMouseMove(event) return false } - mx, my := event.X, event.Y for i := range g.actionableRegions { // Mouse over a game control element if g.actionableRegions[i].Rect.IsInRect(mx, my) { @@ -327,6 +340,26 @@ func (g *GameControls) updateLayout() { // TODO: consider caching the panels to single image that is reused. func (g *GameControls) Render(target d2render.Surface) { + for entityIdx := range *g.mapEngine.Entities() { + entity := (*g.mapEngine.Entities())[entityIdx] + if entity.Name() == "" { + continue + } + + entScreenXf, entScreenYf := g.mapRenderer.WorldToScreenF(entity.GetPositionF()) + entScreenX := int(math.Floor(entScreenXf)) + entScreenY := int(math.Floor(entScreenYf)) + + if ((entScreenX-20) <= g.lastMouseX) && ((entScreenX+20) >= g.lastMouseX) && + ((entScreenY-80) <= g.lastMouseY) && (entScreenY>= g.lastMouseY) { + g.nameLabel.SetText(entity.Name()) + g.nameLabel.SetPosition(entScreenX, entScreenY - 100) + g.nameLabel.Render(target) + break + } + } + + g.inventory.Render(target) g.heroStats.Render(target) g.escapeMenu.Render(target)