// Returns a map from player name to the path of that player's file. func GetAllPlayers() map[string]string { root := filepath.Join(base.GetDataDir(), "players") players := make(map[string]string) filepath.Walk(root, func(path string, info os.FileInfo, err error) error { if info.IsDir() { return nil } f, err := os.Open(path) if err != nil { base.Warn().Printf("Unable to open player file: %s.", path) return nil } defer f.Close() dec := gob.NewDecoder(f) var name string err = dec.Decode(&name) if err != nil { base.Warn().Printf("Unable to read player file: %s.", path) return nil } players[name] = path return nil }) return players }
func (g *Game) TeamLos(side Side, x, y, dx, dy int) bool { var team_los [][]byte if side == SideExplorers { team_los = g.los.intruders.tex.Pix() } else if side == SideHaunt { team_los = g.los.denizens.tex.Pix() } else { base.Warn().Printf("Can only ask for TeamLos for the intruders and denizens.") return false } if team_los == nil { return false } for i := x; i < x+dx; i++ { for j := y; j < y+dy; j++ { if i < 0 || j < 0 || i >= len(team_los) || j >= len(team_los[0]) { continue } if team_los[i][j] >= house.LosVisibilityThreshold { return true } } } return false }
func makeAi(path string, g *game.Game, ent *game.Entity, dst_iface *game.Ai, kind game.AiKind) { ai_struct := new(Ai) ai_struct.path = path var err error ai_struct.watcher, err = fsnotify.NewWatcher() if err != nil { base.Warn().Printf("Unable to create a filewatcher - '%s' will not reload ai files dynamically: %v", path, err) ai_struct.watcher = nil } ai_struct.ent = ent ai_struct.game = g ai_struct.active_set = make(chan bool) ai_struct.active_query = make(chan bool) ai_struct.exec_query = make(chan struct{}) ai_struct.pause = make(chan struct{}) ai_struct.terminate = make(chan struct{}) ai_struct.execs = make(chan game.ActionExec) ai_struct.kind = kind err = ai_struct.setupLuaState() if err != nil { base.Error().Printf("Unable to make ai: %v", err) if ai_struct.watcher != nil { ai_struct.watcher.Close() } dst_iface = nil return } go ai_struct.masterRoutine() *dst_iface = ai_struct }
func UpdatePlayer(p *Player, L *lua.State) { buffer := bytes.NewBuffer(nil) L.GetGlobal("store") err := LuaEncodeTable(buffer, L, -1) if err != nil { base.Warn().Printf("Error encoding lua state: %v", err) } L.Pop(1) p.Lua_store = buffer.Bytes() }
// Returns true iff the action was set // This function will return false if there is no selected entity, if the // action cannot be selected (because it is invalid or the entity has // insufficient Ap), or if there is an action currently executing. func (g *Game) SetCurrentAction(action Action) bool { if g.Action_state != noAction && g.Action_state != preppingAction { return false } // the action should be one that belongs to the current entity, if not then // we need to bail out immediately if g.selected_ent == nil { base.Warn().Printf("Tried to SetCurrentAction() without a selected entity.") return action == nil } if action != nil { valid := false for _, a := range g.selected_ent.Actions { if a == action { valid = true break } } if !valid { base.Warn().Printf("Tried to SetCurrentAction() with an action that did not belong to the selected entity.") return action == nil } } if g.current_action != nil { g.current_action.Cancel() } if action == nil { g.Action_state = noAction } else { g.Action_state = preppingAction } g.viewer.RemoveFloorDrawable(g.current_action) g.current_action = action if g.current_action != nil { g.viewer.AddFloorDrawable(g.current_action) } return true }
func (g *Game) SpawnEntity(spawn *Entity, x, y int) bool { for i := range g.Ents { cx, cy := g.Ents[i].Pos() if cx == x && cy == y { base.Warn().Printf("Can't spawn entity at (%d, %d) - already occupied by '%s'.", x, y, g.Ents[i].Name) return false } } spawn.X = float64(x) spawn.Y = float64(y) spawn.Info.RoomsExplored[spawn.CurrentRoom()] = true g.Ents = append(g.Ents, spawn) return true }
// Distributes the ents among the spawn points. Since this is done randomly // it might not work, so there is a very small chance that not all spawns will // have an ent given to them, even if it is possible to distrbiute them // properly. Regardless, at least some will be spawned. func spawnEnts(g *Game, ents []*Entity, spawns []*house.SpawnPoint) { sort.Sort(orderSpawnsSmallToBig(spawns)) sanity := 100 var places []entSpawnPair for sanity > 0 { sanity-- places = places[0:0] sort.Sort(orderEntsBigToSmall(ents)) //slightly shuffle the ents for i := range ents { j := i + rand.Intn(5) - 2 if j >= 0 && j < len(ents) { ents[i], ents[j] = ents[j], ents[i] } } // Go through each ent and try to place it in an unused spawn point used_spawns := make(map[*house.SpawnPoint]bool) for _, ent := range ents { for _, spawn := range spawns { if used_spawns[spawn] { continue } if spawn.Dx < ent.Dx || spawn.Dy < ent.Dy { continue } used_spawns[spawn] = true places = append(places, entSpawnPair{ent, spawn}) break } } if len(places) == len(spawns) { break } } if sanity > 0 { base.Log().Printf("Placed all objects with %d sanity remaining", sanity) } else { base.Warn().Printf("Only able to place %d out of %d objects", len(places), len(spawns)) } for _, place := range places { place.ent.X = float64(place.spawn.X + rand.Intn(place.spawn.Dx-place.ent.Dx+1)) place.ent.Y = float64(place.spawn.Y + rand.Intn(place.spawn.Dy-place.ent.Dy+1)) g.viewer.AddDrawable(place.ent) g.Ents = append(g.Ents, place.ent) base.Log().Printf("Using object '%s' at (%.0f, %.0f)", place.ent.Name, place.ent.X, place.ent.Y) } }
func MakeSystemMenu(gp *GamePanel, player *Player) (gui.Widget, error) { var sm SystemMenu datadir := base.GetDataDir() err := base.LoadAndProcessObject(filepath.Join(datadir, "ui", "system", "layout.json"), "json", &sm.layout) if err != nil { return nil, err } sm.layout.Main.f = func(interface{}) {} sm.buttons = []ButtonLike{ &sm.layout.Sub.Return, &sm.layout.Sub.Save, } sm.layout.Sub.Return.f = func(_ui interface{}) { ui := _ui.(*gui.Gui) gp.game.Ents = nil gp.game.Think(1) // This should clean things up ui.DropFocus() Restart() } sm.layout.Sub.Save.Entry.text = player.Name sm.layout.Sub.Save.Button.f = func(interface{}) { UpdatePlayer(player, gp.script.L) str, err := base.ToGobToBase64(gp.game) if err != nil { base.Error().Printf("Error gobbing game state: %v", err) return } player.Game_state = str player.Name = sm.layout.Sub.Save.Text() player.No_init = true base.Log().Printf("Saving player: %v", player) err = SavePlayer(player) if err != nil { base.Warn().Printf("Unable to save player: %v", err) return } sm.saved_time = time.Now() sm.saved_alpha = 1.0 } return &sm, nil }
func (t *TextArea) RenderString(s string) { var just gui.Justification switch t.Justification { case "center": just = gui.Center case "left": just = gui.Left case "right": just = gui.Right default: base.Warn().Printf("Unknown justification '%s' in main gui bar.", t.Justification) t.Justification = "center" } px := float64(t.X) py := float64(t.Y) d := base.GetDictionary(t.Size) d.RenderString(s, px, py, 0, d.MaxHeight(), just) }
func main() { defer func() { if r := recover(); r != nil { data := debug.Stack() base.Error().Printf("PANIC: %v\n", r) base.Error().Printf("PANIC: %s\n", string(data)) base.CloseLog() fmt.Printf("PANIC: %s\n", string(data)) } }() base.Log().Printf("Version %s", Version()) sys.Startup() sound.Init() render.Init() render.Queue(func() { sys.CreateWindow(10, 10, wdx, wdy) sys.EnableVSync(true) err := gl.Init() if err != nil { panic(err) } gl.Enable(gl.BLEND) gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) }) base.InitShaders() runtime.GOMAXPROCS(8) ui, err := gui.Make(gin.In(), gui.Dims{wdx, wdy}, filepath.Join(datadir, "fonts", "skia.ttf")) if err != nil { panic(err.Error()) } loadAllRegistries() // TODO: Might want to be able to reload stuff, but this is sensitive because it // is loading textures. We should probably redo the sprite system so that this // is easier to safely handle. game.LoadAllEntities() // Set up editors editors = map[string]house.Editor{ "room": house.MakeRoomEditorPanel(), "house": house.MakeHouseEditorPanel(), } for name, editor := range editors { path := base.GetStoreVal(fmt.Sprintf("last %s path", name)) path = filepath.Join(datadir, path) if path != "" { editor.Load(path) } } editor_name = "room" editor = editors[editor_name] edit_mode := false game.Restart = func() { base.Log().Printf("Restarting...") ui.RemoveChild(game_box) game_box = &lowerLeftTable{gui.MakeAnchorBox(gui.Dims{1024, 768})} err = game.InsertStartMenu(game_box) if err != nil { panic(err) } ui.AddChild(game_box) base.Log().Printf("Restarted") } game.Restart() if base.IsDevel() { ui.AddChild(base.MakeConsole()) } sys.Think() // Wait until now to create the dictionary because the render thread needs // to be running in advance. render.Queue(func() { ui.Draw() }) render.Purge() var profile_output *os.File heap_prof_count := 0 for key_map["quit"].FramePressCount() == 0 { sys.Think() render.Queue(func() { sys.SwapBuffers() ui.Draw() }) render.Purge() for _, child := range game_box.GetChildren() { if gp, ok := child.(*game.GamePanel); ok { game_panel = gp } } if base.IsDevel() { if key_map["cpu profile"].FramePressCount() > 0 { if profile_output == nil { profile_output, err = os.Create(filepath.Join(datadir, "cpu.prof")) if err == nil { err = pprof.StartCPUProfile(profile_output) if err != nil { base.Log().Printf("Unable to start CPU profile: %v\n", err) profile_output.Close() profile_output = nil } base.Log().Printf("profout: %v\n", profile_output) } else { base.Log().Printf("Unable to start CPU profile: %v\n", err) } } else { pprof.StopCPUProfile() profile_output.Close() profile_output = nil } } if key_map["heap profile"].FramePressCount() > 0 { out, err := os.Create(filepath.Join(datadir, fmt.Sprintf("heap-%d.prof", heap_prof_count))) heap_prof_count++ if err == nil { err = pprof.WriteHeapProfile(out) out.Close() if err != nil { base.Warn().Printf("Unable to write heap profile: %v", err) } } else { base.Warn().Printf("Unable to create heap profile: %v", err) } } if key_map["manual mem"].FramePressCount() > 0 { base.Log().Printf(memory.TotalAllocations()) } if key_map["game mode"].FramePressCount()%2 == 1 { base.Log().Printf("Game mode change: %t", edit_mode) if edit_mode { ui.RemoveChild(editor) ui.AddChild(game_box) } else { ui.RemoveChild(game_box) ui.AddChild(editor) } edit_mode = !edit_mode if key_map["row up"].FramePressCount() > 0 { house.Num_rows += 25 } if key_map["row down"].FramePressCount() > 0 { house.Num_rows -= 25 } if key_map["steps up"].FramePressCount() > 0 { house.Num_steps++ } if key_map["steps down"].FramePressCount() > 0 { house.Num_steps-- } if key_map["noise up"].FramePressCount() > 0 { house.Noise_rate += 10 } if key_map["noise down"].FramePressCount() > 0 { house.Noise_rate -= 10 } if key_map["foo"].FramePressCount() > 0 { house.Foo = (house.Foo + 1) % 2 } } if edit_mode { editMode() } else { gameMode() } } // Draw a cursor at the cursor - for testing an osx bug in glop. // zx, zy := gin.In().GetCursor("Mouse").Point() // render.Queue(func() { // gl.Color4ub(255, 0, 0, 255) // gl.Begin(gl.LINES) // { // gl.Vertex2i(int32(zx-25), int32(zy)) // gl.Vertex2i(int32(zx+25), int32(zy)) // gl.Vertex2i(int32(zx), int32(zy-25)) // gl.Vertex2i(int32(zx), int32(zy+25)) // } // gl.End() // }) } }
func editMode() { draggingAndZooming(editor.GetViewer()) if ui.FocusWidget() == nil { for name := range editors { if key_map[fmt.Sprintf("%s editor", name)].FramePressCount() > 0 && ui.FocusWidget() == nil { ui.RemoveChild(editor) editor_name = name editor = editors[editor_name] loadAllRegistries() editor.Reload() ui.AddChild(editor) } } if key_map["save"].FramePressCount() > 0 && chooser == nil { path, err := editor.Save() if err != nil { base.Warn().Printf("Failed to save: %v", err.Error()) } if path != "" && err == nil { base.SetStoreVal(fmt.Sprintf("last %s path", editor_name), base.TryRelative(datadir, path)) } } if key_map["load"].FramePressCount() > 0 && chooser == nil { callback := func(path string, err error) { ui.DropFocus() ui.RemoveChild(anchor) chooser = nil anchor = nil err = editor.Load(path) if err != nil { base.Warn().Printf("Failed to load: %v", err.Error()) } else { base.SetStoreVal(fmt.Sprintf("last %s path", editor_name), base.TryRelative(datadir, path)) } } chooser = gui.MakeFileChooser(filepath.Join(datadir, fmt.Sprintf("%ss", editor_name)), callback, gui.MakeFileFilter(fmt.Sprintf(".%s", editor_name))) anchor = gui.MakeAnchorBox(gui.Dims{wdx, wdy}) anchor.AddChild(chooser, gui.Anchor{0.5, 0.5, 0.5, 0.5}) ui.AddChild(anchor) ui.TakeFocus(chooser) } // Don't select tabs in an editor if we're doing some other sort of command ok_to_select := true for _, v := range key_map { if v.FramePressCount() > 0 { ok_to_select = false break } } if ok_to_select { for i := 1; i <= 9; i++ { if gin.In().GetKey(gin.KeyId('0'+i)).FramePressCount() > 0 { editor.SelectTab(i - 1) } } } } }
func (g *Game) Think(dt int64) { for _, ent := range g.Ents { if !g.all_ents_in_game[ent] { g.all_ents_in_game[ent] = true g.all_ents_in_memory[ent] = true } } var mark []*Entity for ent := range g.all_ents_in_memory { if !g.all_ents_in_game[ent] && ent != g.new_ent { mark = append(mark, ent) } } for _, ent := range mark { delete(g.all_ents_in_game, ent) delete(g.all_ents_in_memory, ent) ent.Release() } // Figure out if there are any entities that might be occluded be any // furniture, if so we'll want to make that furniture a little transparent. for _, floor := range g.House.Floors { for _, room := range floor.Rooms { for _, furn := range room.Furniture { if !furn.Blocks_los { continue } rx, ry := room.Pos() x, y2 := furn.Pos() x += rx y2 += ry dx, dy := furn.Dims() x2 := x + dx y := y2 + dy tex := furn.Orientations[furn.Rotation].Texture.Data() tex_dy := 2 * (tex.Dy() * ((x2 - y2) - (x - y))) / tex.Dx() v1 := y - x v2 := y2 - x2 hit := false for _, ent := range g.Ents { ex, ey2 := ent.Pos() edx, edy := ent.Dims() ex2 := ex + edx ey := ey2 + edy if ex+ey2 < x+y2 || ex+ey2 > x+y2+tex_dy { continue } if ent.Side() != g.Side && !g.TeamLos(g.Side, ex, ey2, edx, edy) { continue } ev1 := ey - ex ev2 := ey2 - ex2 if ev2 >= v1 || ev1 <= v2 { continue } hit = true break } alpha := furn.Alpha() if hit { furn.SetAlpha(doApproach(alpha, 0.3, dt)) } else { furn.SetAlpha(doApproach(alpha, 1.0, dt)) } } } } switch g.Turn_state { case turnStateInit: select { case <-g.comm.script_to_game: base.Log().Printf("ScriptComm: change to turnStateStart") g.Turn_state = turnStateStart g.script.OnRound(g) // g.OnRound() default: } case turnStateStart: select { case <-g.comm.script_to_game: base.Log().Printf("ScriptComm: change to turnStateAiAction") g.Turn_state = turnStateAiAction default: } case turnStateScriptOnAction: select { case exec := <-g.comm.script_to_game: if g.current_exec != nil { base.Error().Printf("Got an exec from the script when we already had one pending.") } else { if exec != nil { g.current_exec = exec.(ActionExec) } else { base.Log().Printf("ScriptComm: change to turnStateAiAction") g.Turn_state = turnStateAiAction } } default: } case turnStateMainPhaseOver: select { case exec := <-g.comm.script_to_game: if exec != nil { base.Log().Printf("ScriptComm: Got an exec: %v", exec) g.current_exec = exec.(ActionExec) g.Action_state = doingAction ent := g.EntityById(g.current_exec.EntityId()) g.viewer.RemoveFloorDrawable(g.current_action) g.current_action = ent.Actions[g.current_exec.ActionIndex()] g.viewer.AddFloorDrawable(g.current_action) ent.current_action = g.current_action } else { g.Turn_state = turnStateEnd base.Log().Printf("ScriptComm: change to turnStateEnd for realzes") } default: } case turnStateEnd: select { case <-g.comm.script_to_game: g.Turn_state = turnStateStart base.Log().Printf("ScriptComm: change to turnStateStart") g.OnRound(true) default: } } if g.current_exec != nil && g.Action_state != verifyingAction && g.Turn_state != turnStateMainPhaseOver { ent := g.EntityById(g.current_exec.EntityId()) g.viewer.RemoveFloorDrawable(g.current_action) g.current_action = ent.Actions[g.current_exec.ActionIndex()] g.viewer.AddFloorDrawable(g.current_action) ent.current_action = g.current_action g.Action_state = verifyingAction base.Log().Printf("ScriptComm: request exec verification") g.comm.game_to_script <- g.current_exec } if g.Action_state == verifyingAction { select { case <-g.comm.script_to_game: g.Action_state = doingAction default: } } // If there is an action that is currently executing we need to advance that // action. if g.Action_state == doingAction { res := g.current_action.Maintain(dt, g, g.current_exec) if g.current_exec != nil { base.Log().Printf("ScriptComm: sent action") g.current_exec = nil } switch res { case Complete: g.current_action.Cancel() g.viewer.RemoveFloorDrawable(g.current_action) g.current_action = nil g.Action_state = noAction if g.Turn_state != turnStateMainPhaseOver { g.Turn_state = turnStateScriptOnAction } base.Log().Printf("ScriptComm: Action complete") g.comm.game_to_script <- nil g.checkWinConditions() case InProgress: case CheckForInterrupts: } } for _, ent := range g.Ents { ent.Think(dt) s := ent.Sprite() if s != nil { if s.AnimState() == "ready" && s.Idle() && g.current_action == nil && ent.current_action != nil { g.viewer.RemoveFloorDrawable(g.current_action) ent.current_action = nil } } } if g.new_ent != nil { g.new_ent.Think(dt) } for i := range g.Ents { g.UpdateEntLos(g.Ents[i], false) } if g.los.denizens.mode == LosModeEntities { g.mergeLos(SideHaunt) } if g.los.intruders.mode == LosModeEntities { g.mergeLos(SideExplorers) } // Do spawn points los stuff for _, los := range []*spawnLos{&g.Los_spawns.Denizens, &g.Los_spawns.Intruders} { if los.r == nil || los.r.String() != los.Pattern { if los.Pattern == "" { los.r = nil } else { var err error los.r, err = regexp.Compile("^" + los.Pattern + "$") if err != nil { base.Warn().Printf("Unable to compile regexp: `%s`", los.Pattern) los.Pattern = "" } } } } for i := 0; i < 2; i++ { var los *spawnLos var pix [][]byte if i == 0 { if g.los.denizens.mode == LosModeBlind { continue } los = &g.Los_spawns.Denizens pix = g.los.denizens.tex.Pix() } else { if g.los.intruders.mode == LosModeBlind { continue } los = &g.Los_spawns.Intruders pix = g.los.intruders.tex.Pix() } if los.r == nil { continue } for _, spawn := range g.House.Floors[0].Spawns { if !los.r.MatchString(spawn.Name) { continue } sx, sy := spawn.Pos() dx, dy := spawn.Dims() for x := sx; x < sx+dx; x++ { for y := sy; y < sy+dy; y++ { if pix[x][y] < house.LosVisibilityThreshold { pix[x][y] = house.LosVisibilityThreshold } } } } } for _, tex := range []*house.LosTexture{g.los.denizens.tex, g.los.intruders.tex} { pix := tex.Pix() amt := dt/6 + 1 mod := false for i := range pix { for j := range pix[i] { v := int64(pix[i][j]) if v < house.LosVisibilityThreshold { v -= amt } else { v += amt } if v < house.LosMinVisibility { v = house.LosMinVisibility } if v < 0 { v = 0 } if v > 255 { v = 255 } mod = mod || (byte(v) != pix[i][j]) pix[i][j] = byte(v) } } if mod { tex.Remap() } } // Don't do any ai stuff if there is a pending action if g.current_action != nil { return } // Also don't do an ai stuff if this isn't the appropriate state if g.Turn_state != turnStateAiAction { return } // If any entities are not either ready or dead let's wait until they are // before we do any of the ai stuff for _, ent := range g.Ents { if ent.Side() != SideHaunt && ent.Side() != SideExplorers { // Relics and cleanse points and whatnot matter here, and they might not // be in a 'ready' state. continue } state := ent.sprite.Sprite().AnimState() if state != "ready" && state != "killed" { return } if !ent.sprite.Sprite().Idle() { return } } // Do Ai - if there is any to do if g.Side == SideHaunt { if g.Ai.minions.Active() { g.active_ai = g.Ai.minions g.Action_state = waitingAction } else { if g.Ai.denizens.Active() { g.active_ai = g.Ai.denizens g.Action_state = waitingAction } } } else { if g.Ai.intruders.Active() { g.active_ai = g.Ai.intruders g.Action_state = waitingAction } } if g.Action_state == waitingAction { select { case exec := <-g.active_ai.ActionExecs(): if exec != nil { g.current_exec = exec } else { g.Action_state = noAction // TODO: indicate that the master ai can go now } default: } } if g.player_inactive && g.Action_state == noAction && !g.Ai.intruders.Active() && !g.Ai.denizens.Active() && !g.Ai.minions.Active() { g.Turn_state = turnStateMainPhaseOver base.Log().Printf("ScriptComm: change to turnStateMainPhaseOver") g.comm.game_to_script <- nil base.Log().Printf("ScriptComm: sent nil") } }
// Returns an array of all entities of a specified type that are in this // entity's los. The entities in the array will be sorted in ascending order // of distance from this entity. // Format // ents = nearestNEntites(max, kind) // // Input: // max - integer - Maximum number of entities to return // kind - string - One of "intruder" "denizen" "minion" "servitor" // "master" "non-minion" "non-servitor" "non-master" and // "all". The "non-*" parameters indicate denizens only // (i.e. will *not* include intruders) that are not of the // type specified. // // Output: // ents - array[integer] - Array of entity ids. func NearestNEntitiesFunc(me *game.Entity) lua.GoFunction { valid_kinds := map[string]bool{ "intruder": true, "denizen": true, "minion": true, "servitor": true, "master": true, "object": true, } return func(L *lua.State) int { if !game.LuaCheckParamsOk(L, "NearestNEntities", game.LuaInteger, game.LuaString) { return 0 } g := me.Game() max := L.ToInteger(-2) kind := L.ToString(-1) if !valid_kinds[kind] { err_str := fmt.Sprintf("NearestNEntities expects kind in the set ['intruder' 'denizen' 'servitor' 'master' 'minion'], got %s.", kind) base.Warn().Printf(err_str) L.PushString(err_str) L.Error() return 0 } var eds entityDistSlice for _, ent := range g.Ents { if ent.Stats != nil && ent.Stats.HpCur() <= 0 { continue } switch kind { case "intruder": if ent.Side() != game.SideExplorers { continue } case "denizen": if ent.Side() != game.SideHaunt { continue } case "minion": if ent.HauntEnt == nil || ent.HauntEnt.Level != game.LevelMinion { continue } case "servitor": if ent.HauntEnt == nil || ent.HauntEnt.Level != game.LevelServitor { continue } case "master": if ent.HauntEnt == nil || ent.HauntEnt.Level != game.LevelMaster { continue } case "object": if ent.ObjectEnt == nil { continue } } x, y := ent.Pos() dx, dy := ent.Dims() if !me.HasTeamLos(x, y, dx, dy) { continue } eds = append(eds, entityDist{rangedDistBetween(me, ent), ent}) } // TODO: ONLY GUYS THAT EXIST sort.Sort(eds) if max > len(eds) { max = len(eds) } if max < 0 { max = 0 } eds = eds[0:max] // eds contains the results, in order. Now we make a lua table and // populate it with the entity ids of the results. L.NewTable() for i := range eds { L.PushInteger(i + 1) game.LuaPushEntity(L, eds[i].ent) L.SetTable(-3) } return 1 } }
func (m *MainBar) Draw(region gui.Region) { m.region = region gl.Enable(gl.TEXTURE_2D) m.layout.Background.Data().Bind() gl.Color4d(1, 1, 1, 1) gl.Begin(gl.QUADS) gl.TexCoord2d(0, 0) gl.Vertex2i(region.X, region.Y) gl.TexCoord2d(0, -1) gl.Vertex2i(region.X, region.Y+region.Dy) gl.TexCoord2d(1, -1) gl.Vertex2i(region.X+region.Dx, region.Y+region.Dy) gl.TexCoord2d(1, 0) gl.Vertex2i(region.X+region.Dx, region.Y) gl.End() buttons := m.no_actions_buttons if m.ent != nil && len(m.ent.Actions) > m.layout.Actions.Count { buttons = m.all_buttons } for _, button := range buttons { button.RenderAt(region.X, region.Y) } ent := m.game.HoveredEnt() if ent == nil { ent = m.ent } if ent != nil && ent.Stats != nil { gl.Color4d(1, 1, 1, 1) ent.Still.Data().Bind() tdx := ent.Still.Data().Dx() tdy := ent.Still.Data().Dy() cx := region.X + m.layout.CenterStillFrame.X cy := region.Y + m.layout.CenterStillFrame.Y gl.Begin(gl.QUADS) gl.TexCoord2d(0, 0) gl.Vertex2i(cx-tdx/2, cy-tdy/2) gl.TexCoord2d(0, -1) gl.Vertex2i(cx-tdx/2, cy+tdy/2) gl.TexCoord2d(1, -1) gl.Vertex2i(cx+tdx/2, cy+tdy/2) gl.TexCoord2d(1, 0) gl.Vertex2i(cx+tdx/2, cy-tdy/2) gl.End() m.layout.Name.RenderString(ent.Name) m.layout.Ap.RenderString(fmt.Sprintf("Ap:%d", ent.Stats.ApCur())) m.layout.Hp.RenderString(fmt.Sprintf("Hp:%d", ent.Stats.HpCur())) m.layout.Corpus.RenderString(fmt.Sprintf("Corpus:%d", ent.Stats.Corpus())) m.layout.Ego.RenderString(fmt.Sprintf("Ego:%d", ent.Stats.Ego())) gl.Color4d(1, 1, 1, 1) m.layout.Divider.Data().Bind() tdx = m.layout.Divider.Data().Dx() tdy = m.layout.Divider.Data().Dy() cx = region.X + m.layout.Name.X cy = region.Y + m.layout.Name.Y - 5 gl.Begin(gl.QUADS) gl.TexCoord2d(0, 0) gl.Vertex2i(cx-tdx/2, cy-tdy/2) gl.TexCoord2d(0, -1) gl.Vertex2i(cx-tdx/2, cy+(tdy+1)/2) gl.TexCoord2d(1, -1) gl.Vertex2i(cx+(tdx+1)/2, cy+(tdy+1)/2) gl.TexCoord2d(1, 0) gl.Vertex2i(cx+(tdx+1)/2, cy-tdy/2) gl.End() } if m.ent != nil && m.ent.Stats != nil { // Actions { spacing := m.layout.Actions.Icon_size * float64(m.layout.Actions.Count) spacing = m.layout.Actions.Width - spacing spacing /= float64(m.layout.Actions.Count - 1) m.state.Actions.space = spacing s := m.layout.Actions.Icon_size num_actions := len(m.ent.Actions) xpos := m.layout.Actions.X if num_actions > m.layout.Actions.Count { xpos -= m.state.Actions.scroll_pos * (s + spacing) } d := base.GetDictionary(10) var r gui.Region r.X = int(m.layout.Actions.X) r.Y = int(m.layout.Actions.Y - d.MaxHeight()) r.Dx = int(m.layout.Actions.Width) r.Dy = int(m.layout.Actions.Icon_size + d.MaxHeight()) r.PushClipPlanes() gl.Color4d(1, 1, 1, 1) for i, action := range m.ent.Actions { // Highlight the selected action if action == m.game.current_action { gl.Disable(gl.TEXTURE_2D) gl.Color4d(1, 0, 0, 1) gl.Begin(gl.QUADS) gl.Vertex3d(xpos-2, m.layout.Actions.Y-2, 0) gl.Vertex3d(xpos-2, m.layout.Actions.Y+s+2, 0) gl.Vertex3d(xpos+s+2, m.layout.Actions.Y+s+2, 0) gl.Vertex3d(xpos+s+2, m.layout.Actions.Y-2, 0) gl.End() } gl.Enable(gl.TEXTURE_2D) action.Icon().Data().Bind() if action.Preppable(m.ent, m.game) { gl.Color4d(1, 1, 1, 1) } else { gl.Color4d(0.5, 0.5, 0.5, 1) } gl.Begin(gl.QUADS) gl.TexCoord2d(0, 0) gl.Vertex3d(xpos, m.layout.Actions.Y, 0) gl.TexCoord2d(0, -1) gl.Vertex3d(xpos, m.layout.Actions.Y+s, 0) gl.TexCoord2d(1, -1) gl.Vertex3d(xpos+s, m.layout.Actions.Y+s, 0) gl.TexCoord2d(1, 0) gl.Vertex3d(xpos+s, m.layout.Actions.Y, 0) gl.End() gl.Disable(gl.TEXTURE_2D) ypos := m.layout.Actions.Y - d.MaxHeight() - 2 d.RenderString(fmt.Sprintf("%d", i+1), xpos+s/2, ypos, 0, d.MaxHeight(), gui.Center) xpos += spacing + m.layout.Actions.Icon_size } r.PopClipPlanes() // Now, if there is a selected action, position it between the arrows if m.state.Actions.selected != nil { // a := m.state.Actions.selected d := base.GetDictionary(15) x := m.layout.Actions.X + m.layout.Actions.Width/2 y := float64(m.layout.ActionLeft.Y) str := fmt.Sprintf("%s:%dAP", m.state.Actions.selected.String(), m.state.Actions.selected.AP()) gl.Color4d(1, 1, 1, 1) d.RenderString(str, x, y, 0, d.MaxHeight(), gui.Center) } } // Conditions { gl.Color4d(1, 1, 1, 1) c := m.layout.Conditions d := base.GetDictionary(int(c.Size)) ypos := c.Y + c.Height - d.MaxHeight() + m.state.Conditions.scroll_pos var r gui.Region r.X = int(c.X) r.Y = int(c.Y) r.Dx = int(c.Width) r.Dy = int(c.Height) r.PushClipPlanes() for _, s := range m.ent.Stats.ConditionNames() { d.RenderString(s, c.X+c.Width/2, ypos, 0, d.MaxHeight(), gui.Center) ypos -= float64(d.MaxHeight()) } r.PopClipPlanes() } // Gear if m.ent.ExplorerEnt != nil && m.ent.ExplorerEnt.Gear != nil { gear := m.ent.ExplorerEnt.Gear layout := m.layout.Gear icon := gear.Small_icon.Data() icon.RenderNatural(int(layout.X), int(layout.Y)) d := base.GetDictionary(10) d.RenderString("Gear", layout.X+float64(icon.Dx())/2, layout.Y-d.MaxHeight(), 0, d.MaxHeight(), gui.Center) } } // Mouseover text if m.state.MouseOver.active { var x int switch m.state.MouseOver.location { case mouseOverActions: x = int(m.layout.Actions.X + m.layout.Actions.Width/2) case mouseOverConditions: x = int(m.layout.Conditions.X + m.layout.Conditions.Width/2) case mouseOverGear: default: base.Warn().Printf("Got an unknown mouseover location: %d", m.state.MouseOver.location) m.state.MouseOver.active = false } y := m.layout.Background.Data().Dy() - 40 d := base.GetDictionary(15) d.RenderString(m.state.MouseOver.text, float64(x), float64(y), 0, d.MaxHeight(), gui.Center) } }