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 }
func getLos(gp *GamePanel) lua.GoFunction { return func(L *lua.State) int { if !LuaCheckParamsOk(L, "GetLos", LuaEntity) { return 0 } ent := LuaToEntity(L, gp.game, -1) if ent == nil { base.Error().Printf("Tried to GetLos on an invalid entity.") return 0 } if ent.los == nil || ent.los.grid == nil { base.Error().Printf("Tried to GetLos on an entity without vision.") return 0 } L.NewTable() count := 0 for x := range ent.los.grid { for y := range ent.los.grid[x] { if ent.los.grid[x][y] { count++ L.PushInteger(count) 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 }
func setLosMode(gp *GamePanel) lua.GoFunction { return func(L *lua.State) int { if !LuaCheckParamsOk(L, "SetLosMode", LuaString, LuaAnything) { return 0 } gp.script.syncStart() defer gp.script.syncEnd() side_str := L.ToString(-2) var mode_str string if L.IsString(-1) { mode_str = L.ToString(-1) } else { mode_str = "rooms" } var side Side switch side_str { case "denizens": side = SideHaunt case "intruders": side = SideExplorers default: base.Error().Printf("Cannot pass '%s' as first parameters of setLosMode()", side_str) return 0 } switch mode_str { case "none": gp.game.SetLosMode(side, LosModeNone, nil) case "blind": gp.game.SetLosMode(side, LosModeBlind, nil) case "all": gp.game.SetLosMode(side, LosModeAll, nil) case "entities": gp.game.SetLosMode(side, LosModeEntities, nil) case "rooms": if !L.IsTable(-1) { base.Error().Printf("The last parameter to setLosMode should be an array of rooms if mode == 'rooms'") return 0 } L.PushNil() all_rooms := gp.game.House.Floors[0].Rooms var rooms []*house.Room for L.Next(-2) != 0 { index := L.ToInteger(-1) if index < 0 || index > len(all_rooms) { base.Error().Printf("Tried to reference room #%d which doesn't exist.", index) continue } rooms = append(rooms, all_rooms[index]) L.Pop(1) } gp.game.SetLosMode(side, LosModeRooms, rooms) default: base.Error().Printf("Unknown los mode '%s'", mode_str) return 0 } return 0 } }
func (a *AoeAttack) Maintain(dt int64, g *game.Game, ae game.ActionExec) game.MaintenanceStatus { if ae != nil { a.exec = ae.(*aoeExec) a.targets = a.getTargetsAt(g, a.exec.X, a.exec.Y) if a.Current_ammo > 0 { a.Current_ammo-- } a.ent = g.EntityById(ae.EntityId()) if !a.ent.HasLos(a.exec.X, a.exec.Y, 1, 1) { base.Error().Printf("Entity %d tried to target position (%d, %d) with an aoe but doesn't have los to it: %v", a.ent.Id, a.exec.X, a.exec.Y, a.exec) return game.Complete } if a.Ap > a.ent.Stats.ApCur() { base.Error().Printf("Got an aoe attack that required more ap than available: %v", a.exec) return game.Complete } a.ent.Stats.ApplyDamage(-a.Ap, 0, status.Unspecified) // Track this information for the ais - the attacking ent will only // remember one ent that it hit, but that's ok for _, target := range a.targets { if target.Side() != a.ent.Side() { target.Info.LastEntThatAttackedMe = a.ent.Id a.ent.Info.LastEntThatIAttacked = target.Id } } } if a.ent.Sprite().State() != "ready" { return game.InProgress } for _, target := range a.targets { if target.Stats.HpCur() > 0 && target.Sprite().State() != "ready" { return game.InProgress } } a.ent.TurnToFace(a.exec.X, a.exec.Y) for _, target := range a.targets { target.TurnToFace(a.ent.Pos()) } a.ent.Sprite().Command(a.Animation) for _, target := range a.targets { if g.DoAttack(a.ent, target, a.Strength, a.Kind) { for _, name := range a.Conditions { target.Stats.ApplyCondition(status.MakeCondition(name)) } target.Stats.ApplyDamage(0, -a.Damage, a.Kind) if target.Stats.HpCur() <= 0 { target.Sprite().CommandN([]string{"defend", "killed"}) } else { target.Sprite().CommandN([]string{"defend", "damaged"}) } } else { target.Sprite().CommandN([]string{"defend", "undamaged"}) } } return game.Complete }
func setWaypoint(gp *GamePanel) lua.GoFunction { return func(L *lua.State) int { if !LuaCheckParamsOk(L, "SetWaypoint", LuaString, LuaString, LuaPoint, LuaFloat) { return 0 } gp.script.syncStart() defer gp.script.syncEnd() var wp waypoint side_str := L.ToString(-3) switch side_str { case "intruders": wp.Side = SideExplorers case "denizens": wp.Side = SideHaunt default: base.Error().Printf("Specified '%s' for the side parameter in SetWaypoint, must be 'intruders' or 'denizens'.", side_str) return 0 } wp.Name = L.ToString(-4) // Remove any existing waypoint by the same name algorithm.Choose2(&gp.game.Waypoints, func(w waypoint) bool { return w.Name != wp.Name }) px, py := LuaToPoint(L, -2) wp.X = float64(px) wp.Y = float64(py) wp.Radius = L.ToNumber(-1) gp.game.Waypoints = append(gp.game.Waypoints, wp) return 0 } }
func removeWaypoint(gp *GamePanel) lua.GoFunction { return func(L *lua.State) int { if !LuaCheckParamsOk(L, "RemoveWaypoint", LuaString) { return 0 } gp.script.syncStart() defer gp.script.syncEnd() hit := false name := L.ToString(-1) for i := 0; i < len(gp.game.Waypoints); i++ { if gp.game.Waypoints[i].Name == name { hit = true gp.game.viewer.RemoveFloorDrawable(&gp.game.Waypoints[i]) l := len(gp.game.Waypoints) gp.game.Waypoints[i] = gp.game.Waypoints[l-1] gp.game.Waypoints = gp.game.Waypoints[0 : l-1] } } if !hit { base.Error().Printf("RemoveWaypoint on waypoint '%s' which doesn't exist.", name) return 0 } return 0 } }
func MakeAction(name string) Action { f, ok := action_map[name] if !ok { base.Error().Printf("Unable to find an Action named '%s'", name) } return f() }
func (a *SummonAction) Maintain(dt int64, g *game.Game, ae game.ActionExec) game.MaintenanceStatus { if ae != nil { exec := ae.(*summonExec) ent := g.EntityById(exec.Ent) if ent == nil { base.Error().Printf("Got a summon action without a valid entity.") return game.Complete } a.ent = ent _, a.cx, a.cy = a.ent.Game().FromVertex(exec.Pos) a.ent.Stats.ApplyDamage(-a.Ap, 0, status.Unspecified) a.spawn = game.MakeEntity(a.Ent_name, a.ent.Game()) if a.Current_ammo > 0 { a.Current_ammo-- } } if a.ent.Sprite().State() == "ready" { a.ent.TurnToFace(a.cx, a.cy) a.ent.Sprite().Command(a.Animation) a.spawn.Stats.OnBegin() a.ent.Game().SpawnEntity(a.spawn, a.cx, a.cy) return game.Complete } return game.InProgress }
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 LuaPushSmartFunctionTable(L *lua.State, ft FunctionTable) { // Copy it just in case - I can't imagine someone changing it after passing // it to this function, but I don't want to take any chances. myft := make(FunctionTable) for n, f := range ft { myft[n] = f } names := make([]string, len(myft))[0:0] for name := range myft { names = append(names, name) } sort.Strings(names) valid_selectors := "[" for i, name := range names { if i > 0 { valid_selectors += ", " } valid_selectors += fmt.Sprintf("'%s'", name) } valid_selectors += "]." L.NewTable() L.PushString("__index") L.PushGoFunctionAsCFunction(func(L *lua.State) int { name := L.ToString(-1) if f, ok := myft[name]; ok { f() } else { base.Error().Printf("'%s' is not a valid selector, valid seletors are %s", name, valid_selectors) L.PushNil() } return 1 }) L.SetTable(-3) }
func PopSpawnRegexp() { if len(spawn_regex) == 0 { base.Error().Printf("Tried to pop an empty stack.") return } spawn_regex = spawn_regex[0 : len(spawn_regex)-1] }
func loadHouse(gp *GamePanel) lua.GoFunction { return func(L *lua.State) int { if !LuaCheckParamsOk(L, "LoadHouse", LuaString) { return 0 } gp.script.syncStart() defer gp.script.syncEnd() name := L.ToString(-1) def := house.MakeHouseFromName(name) if def == nil || len(def.Floors) == 0 { base.Error().Printf("No house exists with the name '%s'.", name) return 0 } gp.game = makeGame(def) gp.game.viewer.Edit_mode = true gp.game.script = gp.script base.Log().Printf("script = %p", gp.game.script) gp.AnchorBox = gui.MakeAnchorBox(gui.Dims{1024, 768}) gp.AnchorBox.AddChild(gp.game.viewer, gui.Anchor{0.5, 0.5, 0.5, 0.5}) gp.AnchorBox.AddChild(MakeOverlay(gp.game), gui.Anchor{0.5, 0.5, 0.5, 0.5}) base.Log().Printf("Done making stuff") return 0 } }
func InsertStartMenu(ui gui.WidgetParent) error { var sm StartMenu datadir := base.GetDataDir() err := base.LoadAndProcessObject(filepath.Join(datadir, "ui", "start", "layout.json"), "json", &sm.layout) if err != nil { return err } sm.buttons = []ButtonLike{ &sm.layout.Menu.Continue, &sm.layout.Menu.Versus, &sm.layout.Menu.Online, &sm.layout.Menu.Settings, } sm.layout.Menu.Continue.f = func(interface{}) {} sm.layout.Menu.Versus.f = func(interface{}) { ui.RemoveChild(&sm) ui.AddChild(MakeGamePanel("versus/basic.lua", nil, nil)) } sm.layout.Menu.Settings.f = func(interface{}) {} sm.layout.Menu.Online.f = func(interface{}) { ui.RemoveChild(&sm) err := InsertOnlineMenu(ui) if err != nil { base.Error().Printf("Unable to make Online Menu: %v", err) return } } ui.AddChild(&sm) return nil }
func chooserFromFile(gp *GamePanel) lua.GoFunction { return func(L *lua.State) int { if !LuaCheckParamsOk(L, "ChooserFromFile", LuaString) { return 0 } gp.script.syncStart() defer gp.script.syncEnd() path := filepath.Join(base.GetDataDir(), L.ToString(-1)) chooser, done, err := makeChooserFromOptionBasicsFile(path) if err != nil { base.Error().Printf("Error making chooser: %v", err) return 0 } gp.AnchorBox.AddChild(chooser, gui.Anchor{0.5, 0.5, 0.5, 0.5}) gp.script.syncEnd() res := <-done L.NewTable() for i, s := range res { L.PushInteger(i + 1) L.PushString(s) L.SetTable(-3) } gp.script.syncStart() gp.AnchorBox.RemoveChild(chooser) return 1 } }
func MakeCondition(name string) Condition { maker, ok := condition_makers[name] if !ok { base.Error().Printf("Unable to find a Condition named '%s'", name) return condition_makers["Error"]() } return maker() }
func (ei *entityDef) Dims() (int, int) { if ei.Dx <= 0 || ei.Dy <= 0 { base.Error().Printf("Entity '%s' didn't have its Dims set properly", ei.Name) ei.Dx = 1 ei.Dy = 1 } return ei.Dx, ei.Dy }
func decodeActionExec(b []byte) ActionExec { var ae ActionExec dec := gob.NewDecoder(bytes.NewReader(b)) err := dec.Decode(&ae) if err != nil { base.Error().Printf("Failed to ungob an ActionExec: %v", err) return nil } return ae }
func encodeActionExec(ae ActionExec) []byte { b := bytes.NewBuffer(nil) enc := gob.NewEncoder(b) err := enc.Encode(ae) if err != nil { base.Error().Printf("Failed to gob an ActionExec: %v", err) return nil } return b.Bytes() }
func selectEnt(gp *GamePanel) lua.GoFunction { return func(L *lua.State) int { if !LuaCheckParamsOk(L, "SelectEnt", LuaEntity) { return 0 } gp.script.syncStart() defer gp.script.syncEnd() ent := LuaToEntity(L, gp.game, -1) if ent == nil { base.Error().Printf("Tried to SelectEnt on a non-existent entity.") return 0 } if ent.Side() != gp.game.Side { base.Error().Printf("Tried to SelectEnt on an entity that's not on the current side.") return 0 } gp.game.SelectEnt(ent) return 0 } }
func PushSpawnRegexp(pattern string) { re, err := regexp.Compile(pattern) if err != nil { base.Error().Printf("Unable to compile regexp: '%s': %v", pattern, err) // Just duplicate the top one, since this will probably come with an // associated pop. spawn_regex = append(spawn_regex, topSpawnRegexp()) return } spawn_regex = append(spawn_regex, re) }
func (g *Game) SetVisibility(side Side) { switch side { case SideHaunt: g.viewer.Los_tex = g.los.denizens.tex case SideExplorers: g.viewer.Los_tex = g.los.intruders.tex default: base.Error().Printf("Unable to SetVisibility for side == %d.", side) return } }
func PlaySound(name string) { if system == nil { return } sound, err := system.GetEvent(name, fmod.MODE_DEFAULT) if err != nil { base.Error().Printf("Unable to get event '%s': %v", name, err) return } sound.Start() }
func (bae *BasicActionExec) SetBasicData(ent *Entity, action Action) { bae.Ent = ent.Id bae.Index = -1 for i := range ent.Actions { if ent.Actions[i] == action { bae.Index = i } } if bae.Index == -1 { base.Error().Printf("Action '%v' was unable to find itself in Entity %v's Actions: %v", action, ent, ent.Actions) } }
func (e *Entity) SetGear(gear_name string) bool { if e.ExplorerEnt == nil { base.Error().Printf("Tried to set gear on a non-explorer entity.") return false } if e.ExplorerEnt.Gear != nil && gear_name != "" { base.Error().Printf("Tried to set gear on an explorer that already had gear.") return false } if e.ExplorerEnt.Gear == nil && gear_name == "" { base.Error().Printf("Tried to remove gear from an explorer with no gear.") return false } if gear_name == "" { algorithm.Choose2(&e.Actions, func(a Action) bool { return a.String() != e.ExplorerEnt.Gear.Action }) if e.ExplorerEnt.Gear.Condition != "" { e.Stats.RemoveCondition(e.ExplorerEnt.Gear.Condition) } e.ExplorerEnt.Gear = nil return true } var g Gear g.Defname = gear_name base.GetObject("gear", &g) if g.Name == "" { base.Error().Printf("Tried to load gear '%s' that doesn't exist.", gear_name) return false } e.ExplorerEnt.Gear = &g if g.Action != "" { e.Actions = append(e.Actions, MakeAction(g.Action)) } if g.Condition != "" { e.Stats.ApplyCondition(status.MakeCondition(g.Condition)) } return true }
func (ei *entityDef) Side() Side { types := 0 if ei.ExplorerEnt != nil { types++ } if ei.HauntEnt != nil { types++ } if ei.ObjectEnt != nil { types++ } if types > 1 { base.Error().Printf("Entity '%s' must specify exactly zero or one ent type.", ei.Name) return SideNone } switch { case ei.ExplorerEnt != nil: return SideExplorers case ei.HauntEnt != nil: switch ei.HauntEnt.Level { case LevelMinion: case LevelMaster: case LevelServitor: default: base.Error().Printf("Entity '%s' speciied unknown level '%s'.", ei.Name, ei.HauntEnt.Level) } return SideHaunt case ei.ObjectEnt != nil: return SideObject default: return SideNpc } return SideNone }
func doExec(gp *GamePanel) lua.GoFunction { return func(L *lua.State) int { if !LuaCheckParamsOk(L, "DoExec", LuaTable) { return 0 } L.PushString("__encoded") L.GetTable(-2) str := L.ToString(-1) L.Pop(1) var execs []ActionExec base.Log().Printf("Decoding from: '%s'", str) err := base.FromBase64FromGob(&execs, str) if err != nil { base.Error().Printf("Error decoding exec: %v", err) return 0 } if len(execs) != 1 { base.Error().Printf("Error decoding exec: Found %d execs instead of exactly 1.", len(execs)) return 0 } base.Log().Printf("ScriptComm: Exec: %v", execs[0]) gp.game.comm.script_to_game <- execs[0] base.Log().Printf("ScriptComm: Sent exec") <-gp.game.comm.game_to_script base.Log().Printf("ScriptComm: exec done") done := make(chan bool) gp.script.syncStart() go func() { for i := range gp.game.Ents { gp.game.Ents[i].Sprite().Wait([]string{"ready", "killed"}) } done <- true }() gp.script.syncEnd() <-done return 0 } }
func dialogBox(gp *GamePanel) lua.GoFunction { return func(L *lua.State) int { if L.GetTop() == 1 { if !LuaCheckParamsOk(L, "DialogBox", LuaString) { return 0 } } else { if !LuaCheckParamsOk(L, "DialogBox", LuaString, LuaTable) { return 0 } } gp.script.syncStart() defer gp.script.syncEnd() path := L.ToString(1) var args map[string]string if L.GetTop() > 1 { args = make(map[string]string) L.PushValue(2) L.PushNil() for L.Next(-2) != 0 { args[L.ToString(-2)] = L.ToString(-1) L.Pop(1) } L.Pop(1) } box, output, err := MakeDialogBox(filepath.ToSlash(path), args) if err != nil { base.Error().Printf("Error making dialog: %v", err) return 0 } gp.AnchorBox.AddChild(box, gui.Anchor{0.5, 0.5, 0.5, 0.5}) gp.script.syncEnd() var choices []string for choice := range output { choices = append(choices, choice) } base.Log().Printf("Dialog box press: %v", choices) gp.script.syncStart() gp.AnchorBox.RemoveChild(box) L.NewTable() for i, choice := range choices { L.PushInteger(i + 1) L.PushString(choice) L.SetTable(-3) } return 1 } }
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 (g *Game) mergeLos(side Side) { var pix [][]byte switch side { case SideHaunt: pix = g.los.denizens.tex.Pix() case SideExplorers: pix = g.los.intruders.tex.Pix() default: base.Error().Printf("Unable to mergeLos on side %d.", side) return } for i := range g.los.full_merger { g.los.full_merger[i] = false } for _, ent := range g.Ents { if ent.Side() != side && !ent.Enemy_los { continue } if ent.los == nil { continue } for i := ent.los.minx; i <= ent.los.maxx; i++ { for j := ent.los.miny; j <= ent.los.maxy; j++ { if ent.los.grid[i][j] { g.los.merger[i][j] = true } } } } for i := 0; i < len(pix); i++ { for j := 0; j < len(pix); j++ { if g.los.merger[i][j] { continue } if pix[i][j] >= house.LosVisibilityThreshold { pix[i][j] = house.LosVisibilityThreshold - 1 } } } for i := range g.los.merger { for j := range g.los.merger[i] { if g.los.merger[i][j] { if pix[i][j] < house.LosVisibilityThreshold { pix[i][j] = house.LosVisibilityThreshold } } } } }