func (a *Move) AiMoveToPos(ent *game.Entity, dst []int, max_ap int) game.ActionExec { base.Log().Printf("PATH: Request move to %v", dst) graph := ent.Game().Graph(ent.Side(), false, nil) src := []int{ent.Game().ToVertex(ent.Pos())} _, path := algorithm.Dijkstra(graph, src, dst) base.Log().Printf("PATH: Found path of length %d", len(path)) ppx, ppy := ent.Pos() if path == nil { return nil } _, xx, yy := ent.Game().FromVertex(path[len(path)-1]) base.Log().Printf("PATH: %d,%d -> %d,%d", ppx, ppy, xx, yy) if ent.Stats.ApCur() < max_ap { max_ap = ent.Stats.ApCur() } path = limitPath(ent, src[0], path, max_ap) _, xx, yy = ent.Game().FromVertex(path[len(path)-1]) base.Log().Printf("PATH: (limited) %d,%d -> %d,%d", ppx, ppy, xx, yy) if len(path) <= 1 { return nil } var exec moveExec exec.SetBasicData(ent, a) exec.Path = path return &exec }
func (exec *moveExec) measureCost(ent *game.Entity, g *game.Game) int { if len(exec.Path) == 0 { base.Error().Printf("Zero length path") return -1 } if g.ToVertex(ent.Pos()) != exec.Path[0] { base.Error().Printf("Path doesn't begin at ent's position, %d != %d", g.ToVertex(ent.Pos()), exec.Path[0]) return -1 } graph := g.Graph(ent.Side(), true, nil) v := g.ToVertex(ent.Pos()) cost := 0 for _, step := range exec.Path[1:] { dsts, costs := graph.Adjacent(v) ok := false prev := v base.Log().Printf("Adj(%d):", v) for j := range dsts { base.Log().Printf("Node %d", dsts[j]) if dsts[j] == step { cost += int(costs[j]) v = dsts[j] ok = true break } } base.Log().Printf("%d -> %d: %t", prev, v, ok) if !ok { return -1 } } return cost }
// Performs a move action to the closest one of any of the specifed inputs // points. The movement can be restricted to not spend more than a certain // amount of ap. // Format: // success, p = DoMove(dsts, max_ap) // // Input: // dsts - array[table[x,y]] - Array of all points that are acceptable // destinations. // max_ap - integer - Maxmium ap to spend while doing this move, if the // required ap exceeds this the entity will still move // as far as possible towards a destination. // // Output: // success = bool - True iff the move made it to a position in dsts. // p - table[x,y] - New position of this entity, or nil if the move failed. func DoMoveFunc(a *Ai) lua.GoFunction { return func(L *lua.State) int { if !game.LuaCheckParamsOk(L, "DoMove", game.LuaArray, game.LuaInteger) { return 0 } me := a.ent max_ap := L.ToInteger(-1) L.Pop(1) cur_ap := me.Stats.ApCur() if max_ap > cur_ap { max_ap = cur_ap } n := int(L.ObjLen(-1)) dsts := make([]int, n)[0:0] for i := 1; i <= n; i++ { L.PushInteger(i) L.GetTable(-2) x, y := game.LuaToPoint(L, -1) dsts = append(dsts, me.Game().ToVertex(x, y)) L.Pop(1) } var move *actions.Move var ok bool for i := range me.Actions { move, ok = me.Actions[i].(*actions.Move) if ok { break } } if !ok { // TODO: what to do here? This poor guy didn't have a move action :( L.PushNil() L.PushNil() return 2 } exec := move.AiMoveToPos(me, dsts, max_ap) if exec != nil { a.execs <- exec <-a.pause // TODO: Need to get a resolution x, y := me.Pos() v := me.Game().ToVertex(x, y) complete := false for i := range dsts { if v == dsts[i] { complete = true break } } L.PushBoolean(complete) game.LuaPushPoint(L, x, y) base.Log().Printf("Finished move") } else { base.Log().Printf("Didn't bother moving") L.PushBoolean(true) L.PushNil() } return 2 } }
func (a *AoeAttack) AiAttackPosition(ent *game.Entity, x, y int) game.ActionExec { if !ent.HasLos(x, y, 1, 1) { base.Log().Printf("Don't have los") return nil } if a.Ap > ent.Stats.ApCur() { base.Log().Printf("Don't have the ap") return nil } var exec aoeExec exec.SetBasicData(ent, a) exec.X, exec.Y = x, y return &exec }
// Returns an array of all points that can be reached by walking from a // specific location that end in a certain general area. Assumes that a 1x1 // unit is doing the walking. // Format: // points = AllPathablePoints(src, dst, min, max) // // Inputs: // src - table[x,y] - Where the path starts. // dst - table[x,y] - Another point near where the path should go. // min - integer - Minimum distance from dst that the path should end at. // max - integer - Maximum distance from dst that the path should end at. // // Outputs: // points - array[table[x,y]] func AllPathablePointsFunc(a *Ai) lua.GoFunction { return func(L *lua.State) int { if !game.LuaCheckParamsOk(L, "AllPathablePoints", game.LuaPoint, game.LuaPoint, game.LuaInteger, game.LuaInteger) { return 0 } min := L.ToInteger(-2) max := L.ToInteger(-1) x1, y1 := game.LuaToPoint(L, -4) x2, y2 := game.LuaToPoint(L, -3) a.ent.Game().DetermineLos(x2, y2, max, grid) var dst []int for x := x2 - max; x <= x2+max; x++ { for y := y2 - max; y <= y2+max; y++ { if x > x2-min && x < x2+min && y > y2-min && y < y2+min { continue } if x < 0 || y < 0 || x >= len(grid) || y >= len(grid[0]) { continue } if !grid[x][y] { continue } dst = append(dst, a.ent.Game().ToVertex(x, y)) } } vis := 0 for i := range grid { for j := range grid[i] { if grid[i][j] { vis++ } } } base.Log().Printf("Visible: %d", vis) graph := a.ent.Game().Graph(a.ent.Side(), true, nil) src := []int{a.ent.Game().ToVertex(x1, y1)} reachable := algorithm.ReachableDestinations(graph, src, dst) L.NewTable() base.Log().Printf("%d/%d reachable from (%d, %d) -> (%d, %d)", len(reachable), len(dst), x1, y1, x2, y2) for i, v := range reachable { _, x, y := a.ent.Game().FromVertex(v) L.PushInteger(i + 1) game.LuaPushPoint(L, x, y) L.SetTable(-3) } return 1 } }
func InsertVersusMenu(ui gui.WidgetParent, replace func(gui.WidgetParent) error) error { // return doChooserMenu(ui, makeChooseVersusMetaMenu, replace, inserter(insertGoalMenu)) chooser, done, err := makeChooseVersusMetaMenu() if err != nil { return err } ui.AddChild(chooser) go func() { m := <-done ui.RemoveChild(chooser) if m != nil && len(m) == 1 { base.Log().Printf("Chose: %v", m) switch m[0] { case "Select House": ui.AddChild(MakeGamePanel("versus/basic.lua", nil, map[string]string{"map": "select"}, "")) case "Random House": ui.AddChild(MakeGamePanel("versus/basic.lua", nil, map[string]string{"map": "random"}, "")) case "Continue": ui.AddChild(MakeGamePanel("versus/basic.lua", nil, map[string]string{"map": "continue"}, "")) default: base.Error().Printf("Unknown meta choice '%s'", m[0]) return } } else { err := replace(ui) if err != nil { base.Error().Printf("Error replacing menu: %v", err) } } }() return nil }
// 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) } }
// Decodes a value from the reader and pushes it onto the stack func LuaDecodeValue(r io.Reader, L *lua.State, g *Game) error { var le luaEncodable err := binary.Read(r, binary.LittleEndian, &le) if err != nil { return err } switch le { case luaEncBool: var v byte err = binary.Read(r, binary.LittleEndian, &v) L.PushBoolean(v == 1) case luaEncNumber: var f float64 err = binary.Read(r, binary.LittleEndian, &f) L.PushNumber(f) case luaEncNil: L.PushNil() case luaEncEntity: var id uint64 err = binary.Read(r, binary.LittleEndian, &id) ent := g.EntityById(EntityId(id)) LuaPushEntity(L, ent) if ent != nil { base.Log().Printf("LUA: Push Ent %s", ent.Name) } else { base.Log().Printf("LUA: Push Ent NIL") } case luaEncTable: err = LuaDecodeTable(r, L, g) case luaEncString: var length uint32 err = binary.Read(r, binary.LittleEndian, &length) if err != nil { return err } sb := make([]byte, length) err = binary.Read(r, binary.LittleEndian, &sb) L.PushString(string(sb)) default: return errors.New(fmt.Sprintf("Unknown lua value id == %d.", le)) } if err != nil { return err } return nil }
func (e *Entity) LoadAi() { filename := e.Ai_path.String() if e.Ai_file_override != "" { filename = e.Ai_file_override.String() } if filename == "" { base.Log().Printf("No ai for %s", e.Name) e.Ai = inactiveAi{} return } ai_maker(filename, e.Game(), e, &e.Ai, EntityAi) if e.Ai == nil { e.Ai = inactiveAi{} base.Log().Printf("Failed to make Ai for '%s' with %s", e.Name, filename) } else { base.Log().Printf("Made Ai for '%s' with %s", e.Name, filename) } }
func (a *Ai) Activate() { if a.ent != nil { base.Log().Printf("Activated entity: %v", a.ent.Name) } reload := false for { select { case <-a.watcher.Event: reload = true default: goto no_more_events } } no_more_events: if reload { a.setupLuaState() base.Log().Printf("Reloaded lua state for '%p'", a) } a.active_set <- true }
func (g *Game) checkWinConditions() { return // Check for explorer win conditions explorer_win := false if explorer_win { base.Log().Printf("Explorers won - kaboom") } // Check for haunt win condition - all intruders dead haunts_win := true for i := range g.Ents { if g.Ents[i].Side() == SideExplorers { haunts_win = false } } if haunts_win { base.Log().Printf("Haunts won - kaboom") } }
func (a *Move) Maintain(dt int64, g *game.Game, ae game.ActionExec) game.MaintenanceStatus { if ae != nil { exec := ae.(*moveExec) a.ent = g.EntityById(ae.EntityId()) if len(exec.Path) == 0 { base.Error().Printf("Got a move exec with a path length of 0: %v", exec) return game.Complete } a.cost = exec.measureCost(a.ent, g) if a.cost > a.ent.Stats.ApCur() { base.Error().Printf("Got a move that required more ap than available: %v", exec) base.Error().Printf("Path: %v", exec.Path) return game.Complete } if a.cost == -1 { base.Error().Printf("Got a move that followed an invalid path: %v", exec) base.Error().Printf("Path: %v", exec.Path) if a.ent == nil { base.Error().Printf("ENT was Nil!") } else { x, y := a.ent.Pos() v := g.ToVertex(x, y) base.Error().Printf("Ent pos: (%d, %d) -> (%d)", x, y, v) } return game.Complete } algorithm.Map2(exec.Path, &a.path, func(v int) [2]int { _, x, y := g.FromVertex(v) return [2]int{x, y} }) base.Log().Printf("Path Validated: %v", exec) a.ent.Stats.ApplyDamage(-a.cost, 0, status.Unspecified) src := g.ToVertex(a.ent.Pos()) graph := g.Graph(a.ent.Side(), true, nil) a.drawPath(a.ent, g, graph, src) } // Do stuff factor := float32(math.Pow(2, a.ent.Walking_speed)) dist := a.ent.DoAdvance(factor*float32(dt)/200, a.path[0][0], a.path[0][1]) for dist > 0 { if len(a.path) == 1 { a.ent.DoAdvance(0, 0, 0) a.ent.Info.RoomsExplored[a.ent.CurrentRoom()] = true a.ent = nil return game.Complete } a.path = a.path[1:] a.ent.Info.RoomsExplored[a.ent.CurrentRoom()] = true dist = a.ent.DoAdvance(dist, a.path[0][0], a.path[0][1]) } return game.InProgress }
func MakeGamePanel(script string, p *Player, data map[string]string, game_key mrgnet.GameKey) *GamePanel { var gp GamePanel gp.AnchorBox = gui.MakeAnchorBox(gui.Dims{1024, 768}) if p == nil { p = &Player{} } base.Log().Printf("Script path: %s / %s", script, p.Script_path) if script == "" { script = p.Script_path } startGameScript(&gp, script, p, data, game_key) return &gp }
func (rc *RosterChooser) Respond(ui *gui.Gui, group gui.EventGroup) bool { base.Log().Printf("RosterChooser.Respond") if found, event := group.FindEvent('l'); found && event.Type == gin.Press { rc.focus += rc.layout.Num_options return true } if found, event := group.FindEvent('o'); found && event.Type == gin.Press { rc.focus -= rc.layout.Num_options return true } if found, event := group.FindEvent(gin.MouseLButton); found && event.Type == gin.Press { x, y := event.Key.Cursor().Point() gp := gui.Point{x, y} if gp.Inside(rc.render.down) { rc.focus += rc.layout.Num_options return true } else if gp.Inside(rc.render.up) { rc.focus -= rc.layout.Num_options return true } else if gp.Inside(rc.render.all_options) { for i := range rc.render.options { if gp.Inside(rc.render.options[i]) { rc.selector(i, rc.selected, true) return true } } } else if gp.Inside(rc.render.done) { if rc.selector(-1, rc.selected, false) { base.Log().Printf("calling on-complete") rc.on_complete(rc.selected) } return true } else if rc.on_undo != nil && gp.Inside(rc.render.undo) { rc.on_undo() return true } } return false }
func (he *HouseEditor) Load(path string) error { house, err := MakeHouseFromPath(path) if err != nil { return err } base.Log().Printf("Loaded %s\n", path) house.Normalize() he.house = *house he.viewer.SetBounds() for _, tab := range he.widgets { tab.Reload() } return err }
func MakeUiSelectMap(gp *GamePanel) (gui.Widget, <-chan string, error) { var ui UiSelectMap datadir := base.GetDataDir() err := base.LoadAndProcessObject(filepath.Join(datadir, "ui", "select_map", "config.json"), "json", &ui.layout) if err != nil { return nil, nil, err } ui.region.Dx = 1024 ui.region.Dy = 768 var options []hui.Option // TODO: may want to reload the registry on this one? If we want to pik up // new changes to files that is. for _, name := range base.GetAllNamesInRegistry("houses") { var mo MapOption mo.house_def = house.MakeHouseFromName(name) mo.layout = &ui.layout options = append(options, &mo) } out := make(chan string, 2) chooser := hui.MakeRosterChooser(options, hui.SelectExactlyOne, func(m map[int]bool) { var index int base.Log().Printf("On complete: %v", m) for index = range m { out <- options[index].(*MapOption).house_def.Name base.Log().Printf("Sent '%s'", options[index].(*MapOption).house_def.Name) break } base.Log().Printf("Closing") close(out) }, nil) ui.chooser = chooser return &ui, out, nil }
// Tries to place new_ent in the game at its current position. Returns true // on success, false otherwise. // pattern is a regexp that matches only the names of all valid spawn points. func (g *Game) placeEntity(pattern string) bool { if g.new_ent == nil { base.Log().Printf("No new ent") return false } re, err := regexp.Compile(pattern) if err != nil { base.Error().Printf("Failed to compile regexp: '%s': %v", pattern, err) return false } g.new_ent.Info.RoomsExplored[g.new_ent.CurrentRoom()] = true ix, iy := int(g.new_ent.X), int(g.new_ent.Y) idx, idy := g.new_ent.Dims() r, f, _ := g.House.Floors[0].RoomFurnSpawnAtPos(ix, iy) if r == nil || f != nil { return false } for _, e := range g.Ents { x, y := e.Pos() dx, dy := e.Dims() r1 := image.Rect(x, y, x+dx, y+dy) r2 := image.Rect(ix, iy, ix+idx, iy+idy) if r1.Overlaps(r2) { return false } } // Check for spawn points for _, spawn := range g.House.Floors[0].Spawns { if !re.MatchString(spawn.Name) { continue } x, y := spawn.Pos() dx, dy := spawn.Dims() if ix < x || ix+idx > x+dx { continue } if iy < y || iy+idy > y+dy { continue } g.Ents = append(g.Ents, g.new_ent) g.new_ent = nil return true } return false }
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 (gdt *gameDataTransient) alloc() { if gdt.los.denizens.tex != nil { return } gdt.los.denizens.tex = house.MakeLosTexture() gdt.los.intruders.tex = house.MakeLosTexture() gdt.los.full_merger = make([]bool, house.LosTextureSizeSquared) gdt.los.merger = make([][]bool, house.LosTextureSize) for i := range gdt.los.merger { gdt.los.merger[i] = gdt.los.full_merger[i*house.LosTextureSize : (i+1)*house.LosTextureSize] } gdt.comm.script_to_game = make(chan interface{}, 1) gdt.comm.game_to_script = make(chan interface{}, 1) gdt.script = &gameScript{} base.Log().Printf("script = %p", gdt.script) }
func (sm *SystemMenu) Respond(g *gui.Gui, group gui.EventGroup) bool { cursor := group.Events[0].Key.Cursor() if cursor != nil { sm.mx, sm.my = cursor.Point() } if found, event := group.FindEvent(gin.MouseLButton); found && event.Type == gin.Press { if sm.layout.Main.handleClick(sm.mx, sm.my, g) { if sm.focus { g.DropFocus() } else { g.TakeFocus(sm) } sm.focus = true base.Log().Printf("focus: %v %v", sm, g.FocusWidget()) return true } if sm.focus { hit := false for _, button := range sm.buttons { if button.handleClick(sm.mx, sm.my, g) { hit = true } } if hit { return true } } } else { hit := false for _, button := range sm.buttons { if button.Respond(group, nil) { hit = true } } if hit { return true } } return (g.FocusWidget() == sm) }
func init() { runtime.LockOSThread() sys = system.Make(gos.GetSystemInterface()) rand.Seed(100) // TODO: This should not be OS-specific datadir = filepath.Join(os.Args[0], "..", "..") base.SetDatadir(datadir) base.Log().Printf("Setting datadir: %s", datadir) err := house.SetDatadir(datadir) if err != nil { panic(err.Error()) } var key_binds base.KeyBinds base.LoadJson(filepath.Join(datadir, "key_binds.json"), &key_binds) key_map = key_binds.MakeKeyMap() base.SetDefaultKeyMap(key_map) wdx = 1024 wdy = 750 }
func (a *Ai) loadUtils(dir string) { root := filepath.Join(filepath.Join(filepath.Dir(a.path), "utils", dir)) a.watcher.Watch(root) filepath.Walk(root, func(path string, info os.FileInfo, err error) error { if err != nil || info.IsDir() { return nil } if strings.HasSuffix(info.Name(), ".lua") { f, err := os.Open(path) if err != nil { return nil } data, err := ioutil.ReadAll(f) f.Close() if err != nil { return nil } base.Log().Printf("Loaded lua utils file '%s'", path) a.L.DoString(string(data)) } return nil }) }
func (g *Game) UpdateEntLos(ent *Entity, force bool) { if ent.los == nil || ent.Stats == nil { return } ex, ey := ent.Pos() if !force && ex == ent.los.x && ey == ent.los.y { return } base.Log().Printf("UpdateEntLos(%s): %t (%d, %d) -> (%d, %d)", ent.Name, force, ent.los.x, ent.los.y, ex, ey) ent.los.x = ex ent.los.y = ey g.DetermineLos(ex, ey, ent.Stats.Sight(), ent.los.grid) ent.los.minx = len(ent.los.grid) ent.los.miny = len(ent.los.grid) ent.los.maxx = 0 ent.los.maxy = 0 for i := range ent.los.grid { for j := range ent.los.grid[i] { if ent.los.grid[i][j] { if i < ent.los.minx { ent.los.minx = i } if j < ent.los.miny { ent.los.miny = j } if i > ent.los.maxx { ent.los.maxx = i } if j > ent.los.maxy { ent.los.maxy = j } } } } }
func insertGoalMenu(ui gui.WidgetParent, replace replacer) error { chooser, done, err := makeChooseGoalMenu() if err != nil { return err } ui.AddChild(chooser) go func() { m := <-done ui.RemoveChild(chooser) if m != nil { base.Log().Printf("Chose: %v", m) err = insertGoalMenu(ui, replace) if err != nil { base.Error().Printf("Error making goal menu: %v", err) } } else { err := replace(ui) if err != nil { base.Error().Printf("Error replacing menu: %v", err) } } }() return nil }
func doChooserMenu(ui gui.WidgetParent, cm chooserMaker, r replacer, i inserter) error { chooser, done, err := cm() if err != nil { return err } ui.AddChild(chooser) go func() { m := <-done ui.RemoveChild(chooser) if m != nil { base.Log().Printf("Chose: %v", m) err = i(ui, r) if err != nil { base.Error().Printf("Error making menu: %v", err) } } else { err := r(ui) if err != nil { base.Error().Printf("Error replacing menu: %v", err) } } }() return nil }
func limitPath(ent *game.Entity, start int, path []int, max int) []int { total := 0 graph := ent.Game().Graph(ent.Side(), true, nil) for last := 1; last < len(path); last++ { adj, cost := graph.Adjacent(start) found := false for index := range adj { if adj[index] == path[last] { total += int(cost[index]) if total > max { return path[0:last] } start = adj[index] found = true break } } if !found { base.Log().Printf("PATH: DIdn't find, %d / %d", last+1, len(path)) return path[0:last] } } return path }
func Init() { var err error system, err = fmod.EventSystemCreate() if err != nil { base.Error().Printf("Unable to create sound system: %v", err) return } err = system.Init(32, fmod.INIT_NORMAL, nil, fmod.EVENT_INIT_NORMAL) if err != nil { base.Error().Printf("Unable to initialize sound system: %v", err) return } version, _ := system.GetVersion() base.Log().Printf("Fmod version %x", version) err = system.SetMediaPath(filepath.Join(base.GetDataDir(), "sound") + "/") if err != nil { base.Error().Printf("Unable to set media path: %v\n", err) return } err = system.LoadPath("Haunts.fev", nil) if err != nil { base.Error().Printf("Unable to load fev: %v\n", err) return } freq = time.Millisecond * 3 approach = 0.01 music_volume = make(chan float64, 1) music_start = make(chan string, 1) param_control = make(chan paramRequest, 1) music_stop = make(chan bool, 1) go musicControl() }
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 InsertMapChooser(ui gui.WidgetParent, chosen func(string), resert func(ui gui.WidgetParent) error) error { var bops []OptionBasic datadir := base.GetDataDir() err := base.LoadAndProcessObject(filepath.Join(datadir, "ui", "start", "versus", "map_select.json"), "json", &bops) if err != nil { base.Error().Printf("Unable to insert MapChooser: %v", err) return err } var opts []Option algorithm.Map2(bops, &opts, func(ob OptionBasic) Option { return &ob }) for _, opt := range opts { base.Log().Printf(opt.String()) } var ch Chooser err = base.LoadAndProcessObject(filepath.Join(datadir, "ui", "chooser", "layout.json"), "json", &ch.layout) if err != nil { base.Error().Printf("Unable to insert MapChooser: %v", err) return err } ch.options = opts ch.buttons = []*Button{ &ch.layout.Up, &ch.layout.Down, &ch.layout.Back, &ch.layout.Next, } ch.non_scroll_buttons = []*Button{ &ch.layout.Back, &ch.layout.Next, } ch.layout.Up.f = func(interface{}) { ch.layout.Options.Up() } ch.layout.Down.f = func(interface{}) { ch.layout.Options.Down() } ch.selected = make(map[int]bool) ch.layout.Back.f = func(interface{}) { ui.RemoveChild(&ch) err := resert(ui) if err != nil { base.Error().Printf("Unable to make Start Menu: %v", err) return } } ch.layout.Next.f = func(interface{}) { for i := range ch.options { if ch.selected[i] { ui.RemoveChild(&ch) chosen(ch.options[i].String()) } } } ch.layout.Next.valid_func = func() bool { return ch.selector(-1, ch.selected, false) } ch.min, ch.max = 1, 1 if ch.min == 1 && ch.max == 1 { ch.selector = SelectExactlyOne } else { ch.selector = SelectInRange(ch.min, ch.max) } ch.info_region = gui.Region{ gui.Point{ch.layout.Info.X, ch.layout.Info.Y}, gui.Dims{ch.layout.Info.Dx, ch.layout.Info.Dy}, } ui.AddChild(&ch) return nil }
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") } }