func (m UseAbility) Apply(_g interface{}) { g := _g.(*Game) ent, ok := g.Ents[m.Gid] if !ok || ent == nil { base.Error().Printf("Got a use ability that made no sense: %v", m) return } abilities := ent.Abilities() if m.Index < 0 || m.Index >= len(abilities) { base.Error().Printf("Got a UseAbility on index %d with only %d abilities.", m.Index, len(abilities)) return } // Don't use the ability if any other abilities are active anyActive := false for i, ability := range abilities { if i == m.Index { // It's ok to send input to the active ability, so if the ability this // command is trying to use is active that's ok. continue } if ability.IsActive() { anyActive = true break } } if anyActive { return } abilities[m.Index].Input(ent, g, m.Button, m.Trigger) }
func main() { defer base.StackCatcher() sys.Startup() err := gl.Init() if err != nil { base.Error().Fatalf("%v", err) } render.Init() render.Queue(func() { sys.CreateWindow(10, 10, wdx, wdy) sys.EnableVSync(true) }) base.InitShaders() runtime.GOMAXPROCS(10) sys.Think() base.LoadAllDictionaries() if Version() != "standard" { engine := debugHookup(Version()) mainLoop(engine, "standard") } else { // TODO: Reimplement standard hookup } }
func (jm *JotaModule) Param(vs ...runtime.Val) runtime.Val { jm.dieOnTerminated() jm.paramsMutex.Lock() defer jm.paramsMutex.Unlock() paramName := vs[0].String() value, ok := jm.params[paramName] if !ok { return runtime.Nil } switch t := value.(type) { case string: return runtime.String(t) case bool: return runtime.Bool(t) case int: return runtime.Number(t) case float64: return runtime.Number(t) case linear.Vec2: return jm.newVec(t.X, t.Y) case game.Gid: return jm.newEnt(t) default: base.Error().Printf("Requested parameter of unexpected type: %T", t) return runtime.Nil } }
func (m Move) Apply(_g interface{}) { g := _g.(*Game) ent := g.Ents[m.Gid] if ent == nil { return } if ent.Id() != m.Gid { base.Error().Printf("Move.Apply(): %v %v") } ent.Move(m.Angle, m.Magnitude) }
func debugHookup(version string) *cgf.Engine { var err error for false && len(sys.GetActiveDevices()[gin.DeviceTypeController]) < 2 { time.Sleep(time.Millisecond * 100) sys.Think() } var engine *cgf.Engine if version != "host" { engine, err = cgf.NewClientEngine(17, "thunderingvictory.dyndns.org", 20007, base.EmailCrashReport, base.Log()) if err != nil { base.Log().Printf("Unable to connect: %v", err) base.Error().Fatalf("%v", err.Error()) } } else { sys.Think() g := game.MakeGame() if version == "host" { engine, err = cgf.NewHostEngine(g, 17, "", 20007, base.EmailCrashReport, base.Log()) if err != nil { panic(err) } err = cgf.Host(20007, "thunderball") if err != nil { panic(err) } } else { engine, err = cgf.NewLocalEngine(g, 17, base.EmailCrashReport, base.Log()) } if err != nil { base.Error().Fatalf("%v", err.Error()) } } engine.Pause() engine.GetState().(*game.Game).SetSystem(sys) engine.Unpause() base.Log().Printf("Engine Id: %v", engine.Id()) base.Log().Printf("All Ids: %v", engine.Ids()) return engine }
func (b *BaseEnt) BindAi(name string, engine *cgf.Engine) { if b.ai != nil { base.Warn().Printf("Can't bind an Ai when there is already one bound.") return } if b.Gid == "" { base.Error().Printf("Can't bind an Ai on an ent before setting its Gid.") return } b.ai = ai_maker(name, engine, b.Gid) b.ai.Start() }
// Draws everything that is relevant to the players on a computer, but not the // players across the network. Any ui used to determine how to place an object // or use an ability, for example. func (g *Game) RenderLocal(region g2.Region) { switch { case g.Setup != nil: g.RenderLocalSetup(region) case !g.editor.Active(): g.RenderLocalGame(region) case g.editor.Active(): g.RenderLocalEditor(region) default: base.Error().Printf("Unexpected case.") } }
func (g *Game) RenderLocalEditor(region g2.Region) { g.editor.Lock() defer g.editor.Unlock() g.editor.region = region g.editor.camera.regionDims = linear.Vec2{float64(region.Dims.Dx), float64(region.Dims.Dy)} levelDims := linear.Vec2{float64(g.Level.Room.Dx), float64(g.Level.Room.Dy)} g.editor.camera.StandardRegion(levelDims.Scale(0.5), levelDims) g.editor.camera.approachTarget() gl.MatrixMode(gl.PROJECTION) gl.PushMatrix() gl.LoadIdentity() defer gl.PopMatrix() gl.PushAttrib(gl.VIEWPORT_BIT) gl.Viewport(gl.Int(region.X), gl.Int(region.Y), gl.Sizei(region.Dx), gl.Sizei(region.Dy)) defer gl.PopAttrib() current := &g.editor.camera.current gl.Ortho( gl.Double(current.mid.X-current.dims.X/2), gl.Double(current.mid.X+current.dims.X/2), gl.Double(current.mid.Y+current.dims.Y/2), gl.Double(current.mid.Y-current.dims.Y/2), gl.Double(1000), gl.Double(-1000), ) defer func() { gl.MatrixMode(gl.PROJECTION) gl.PopMatrix() gl.MatrixMode(gl.MODELVIEW) }() gl.MatrixMode(gl.MODELVIEW) gl.Enable(gl.BLEND) gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) g.renderWalls() g.renderEdges() g.renderBases() g.renderEntsAndAbilities() g.renderProcesses() g.editor.renderPathing(&g.Level.Room, g.local.pathingData) switch g.editor.action { case editorActionNone: case editorActionPlaceBlock: g.editor.renderPlaceBlock(g) default: base.Error().Printf("Unexpected editorAction: %v", g.editor.action) } }
func (ms *ManaSource) Init(options *ManaSourceOptions) { ms.options = *options if options.NumNodeCols < 2 || options.NumNodeRows < 2 { base.Error().Fatalf(fmt.Sprintf("Invalid options: %v", options)) } r := rand.New(options.Rng) seeds := make([]nodeSeed, options.NumSeeds) for i := range seeds { seed := &seeds[i] seed.x = options.BoardLeft + r.Float64()*(options.BoardRight-options.BoardLeft) seed.y = options.BoardTop + r.Float64()*(options.BoardBottom-options.BoardTop) seed.color = r.Intn(3) } ms.rawNodes = newNodes(options.NumNodeCols * options.NumNodeRows) // ms.rawNodes = make([]node, options.NumNodeCols*options.NumNodeRows) ms.nodes = make([][]node, options.NumNodeCols) for col := 0; col < options.NumNodeCols; col++ { ms.nodes[col] = ms.rawNodes[col*options.NumNodeRows : (col+1)*options.NumNodeRows] for row := 0; row < options.NumNodeRows; row++ { x := options.BoardLeft + float64(col)/float64(options.NumNodeCols-1)*(options.BoardRight-options.BoardLeft) y := options.BoardTop + float64(row)/float64(options.NumNodeRows-1)*(options.BoardBottom-options.BoardTop) maxWeightByColor := [3]float64{0.0, 0.0, 0.0} for _, seed := range seeds { c := seed.color dx := x - seed.x dy := y - seed.y distSquared := dx*dx + dy*dy weight := 1 / (distSquared + 1.0) if weight > maxWeightByColor[c] { maxWeightByColor[c] = weight } } normalizeWeights(options.NodeMagnitude, maxWeightByColor[:]) var weightsCopy [3]float64 copy(weightsCopy[:], maxWeightByColor[:]) ms.nodes[col][row] = node{ X: x, Y: y, RegenPerFrame: options.RegenPerFrame, Mana: maxWeightByColor, MaxMana: weightsCopy, } } } }
func (ai *GameAi) Start() { if ai.jm == nil { return } ctx := runtime.NewCtx(getGlobalJotaResolver(), new(compiler.Compiler)) ctx.RegisterNativeModule(new(stdlib.TimeMod)) ctx.RegisterNativeModule(&LogModule{}) ctx.RegisterNativeModule(ai.jm) mod, err := ctx.Load(ai.jm.name) if err != nil { base.Error().Printf("Error compiling script: %v", err) return } go func() { for { _, err := mod.Run() if err.Error() == "module terminated" { return } base.Error().Printf("Error running script: %v", err) } }() }
func (editor *editorData) saveAction(room *Room) { data, err := json.MarshalIndent(room, "", " ") if err != nil { base.Error().Printf("Unable to encode room to json: %v", err) return } name := fmt.Sprintf("save-%v.json", time.Now()) fullPath := filepath.Join(base.GetDataDir(), name) err = ioutil.WriteFile(fullPath, data, 0664) if err != nil { base.Warn().Printf("Unable to write output json file: %v", err) return } }
func (g *Game) HandleEventGroup(group gin.EventGroup) { g.local.Engine.Pause() defer g.local.Engine.Unpause() switch { case g.Setup != nil: g.HandleEventGroupSetup(group) case g.editor.Active(): g.HandleEventGroupEditor(group) case !g.editor.Active(): g.HandleEventGroupGame(group) default: base.Error().Printf("Unexpected case in HandleEventGroup()") } }
func (ms *ManaSource) regenerateMana() { for i := range ms.rawNodes { node := &ms.rawNodes[i] for c := range node.Mana { if node.MaxMana[c] == 0 { continue } maxRecovery := node.MaxMana[c] * node.RegenPerFrame scale := (node.MaxMana[c] - node.Mana[c]) / node.MaxMana[c] node.Mana[c] += scale * maxRecovery if scale != scale || maxRecovery != maxRecovery { base.Error().Fatalf("NaN showed up somewhere!") } } } }
func (jm *JotaModule) setParam(name string, value interface{}) { jm.paramsMutex.Lock() defer jm.paramsMutex.Unlock() // NOTE: The list of supported types here should match the list in // JotaModule.Param() switch value.(type) { case string: case bool: case int: case float64: case linear.Vec2: case game.Gid: default: base.Error().Printf("Tried to specify a parameter with an unexpected type: %T", value) return } jm.params[name] = value }
func setupSound() { soundInit.Do(func() { var err error // fmodSys, err = fmod.CreateSystem() if err != nil { // base.Error().Fatalf("Unable to initialize fmod: %v", err) } // err = fmodSys.Init(2, 0, nil) if err != nil { // base.Error().Fatalf("Unable to initialize fmod: %v", err) } target := filepath.Join(base.GetDataDir(), "sound/ping.wav") base.Log().Printf("Trying to load ", target) // sound, err = fmodSys.CreateSound_FromFilename(target, fmod.MODE_DEFAULT) if err != nil { base.Error().Fatalf("Unable to load sound: %v", err) } }) }
func (g *Game) AddCreeps(pos linear.Vec2, count, side int, params map[string]interface{}) { if side < 0 || side >= len(g.Level.Room.SideData) { base.Error().Fatalf("Got side %d, but this level only supports sides from 0 to %d.", side, len(g.Level.Room.SideData)-1) return } for i := 0; i < count; i++ { var c CreepEnt c.StatsInst = stats.Make(stats.Base{ Health: 100, Mass: 250, Acc: 50.0, Rate: 0.0, Size: 8, Vision: 400, }) // Evenly space the players on a circle around the starting position. randAngle := rand.New(g.Rng).Float64() * math.Pi rot := (linear.Vec2{15, 0}).Rotate(randAngle + float64(i)*2*3.1415926535/float64(count)) c.Position = pos.Add(rot) c.Side_ = side c.Gid = g.NextGid() c.Abilities_ = append( c.Abilities_, ability_makers["asplode"](map[string]float64{"startRadius": 40, "endRadius": 70, "durationThinks": 50, "dps": 5})) // if playerData.gid[0:2] == "Ai" { // c.BindAi("simple", g.local.Engine) // } g.AddEnt(&c) c.BindAi("creep", g.local.Engine) for name, value := range params { c.ai.SetParam(name, value) } } }
func (g *Game) addPlayersToSide(playerDatas []addPlayerData, side int) { if side < 0 || side >= len(g.Level.Room.SideData) { base.Error().Fatalf("Got side %d, but this level only supports sides from 0 to %d.", len(g.Level.Room.SideData)-1) } for i, playerData := range playerDatas { var p PlayerEnt p.StatsInst = stats.Make(stats.Base{ Health: 1000, Mass: 750, Acc: 150.0, Turn: 0.07, Rate: 0.5, Size: 12, Vision: 500, }) // Evenly space the players on a circle around the starting position. rot := (linear.Vec2{25, 0}).Rotate(float64(i) * 2 * 3.1415926535 / float64(len(playerDatas))) p.Position = g.Level.Room.SideData[side].Base.Add(rot) p.Side_ = side p.Gid = playerData.gid p.Processes = make(map[int]Process) for _, ability := range g.Champs[playerData.champ].Abilities { p.Abilities_ = append( p.Abilities_, ability_makers[ability.Name](ability.Params)) } if playerData.gid[0:2] == "Ai" { // p.BindAi("simple", g.local.Engine) } g.AddEnt(&p) } }
func mainLoop(engine *cgf.Engine, mode string) { defer engine.Kill() var profile_output *os.File var contention_output *os.File var num_mem_profiles int // ui.AddChild(base.MakeConsole()) ticker := time.Tick(time.Millisecond * 17) ui := g2.Make(0, 0, wdx, wdy) ui.AddChild(&game.GameWindow{Engine: engine, Dims: g2.Dims{wdx - 50, wdy - 50}}, g2.AnchorDeadCenter) ui.AddChild(g2.MakeConsole(wdx-50, wdy-50), g2.AnchorDeadCenter) // side0Index := gin.In().BindDerivedKeyFamily("Side0", gin.In().MakeBindingFamily(gin.Key1, []gin.KeyIndex{gin.EitherControl}, []bool{true})) // side1Index := gin.In().BindDerivedKeyFamily("Side1", gin.In().MakeBindingFamily(gin.Key2, []gin.KeyIndex{gin.EitherControl}, []bool{true})) // side2Index := gin.In().BindDerivedKeyFamily("Side2", gin.In().MakeBindingFamily(gin.Key3, []gin.KeyIndex{gin.EitherControl}, []bool{true})) // side0Key := gin.In().GetKeyFlat(side0Index, gin.DeviceTypeAny, gin.DeviceIndexAny) // side1Key := gin.In().GetKeyFlat(side1Index, gin.DeviceTypeAny, gin.DeviceIndexAny) // side2Key := gin.In().GetKeyFlat(side2Index, gin.DeviceTypeAny, gin.DeviceIndexAny) defer ui.StopEventListening() for { <-ticker if gin.In().GetKey(gin.AnyEscape).FramePressCount() != 0 { return } start := time.Now() sys.Think() start = time.Now() render.Queue(func() { ui.Draw() }) render.Queue(func() { start = time.Now() sys.SwapBuffers() }) render.Purge() start = time.Now() // TODO: Replace the 'P' key with an appropriate keybind var err error if gin.In().GetKey(gin.AnyKeyP).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("cpu prof: %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 gin.In().GetKey(gin.AnyKeyL).FramePressCount() > 0 { if contention_output == nil { contention_output, err = os.Create(filepath.Join(datadir, "contention.prof")) if err == nil { runtime.SetBlockProfileRate(1) base.Log().Printf("contention prof: %v\n", contention_output) } else { base.Log().Printf("Unable to start contention profile: %v\n", err) } } else { pprof.Lookup("block").WriteTo(contention_output, 0) contention_output.Close() contention_output = nil } } // TODO: Replace the 'M' key with an appropriate keybind if gin.In().GetKey(gin.AnyKeyM).FramePressCount() > 0 { f, err := os.Create(filepath.Join(datadir, fmt.Sprintf("mem.%d.prof", num_mem_profiles))) if err != nil { base.Error().Printf("Unable to write mem profile: %v", err) } pprof.WriteHeapProfile(f) f.Close() num_mem_profiles++ } } }
func (u SetupComplete) Apply(_g interface{}) { g := _g.(*Game) if g.Setup == nil { return } sideCount := make(map[int]int) // Must have at least two sides sideCount[0] = 0 sideCount[1] = 0 for _, spd := range g.Setup.Players { sideCount[spd.Side]++ } g.Engines = make(map[int64]*PlayerData) for id, player := range g.Setup.Players { var gid Gid if id < 0 { gid = Gid(fmt.Sprintf("Ai:%d", -id)) } else { gid = Gid(fmt.Sprintf("Engine:%d", id)) } g.Engines[id] = &PlayerData{ PlayerGid: Gid(gid), Side: player.Side, ChampIndex: player.ChampIndex, } } // Now that we have the information we can set up a lot of the local data for // this engine's player. if g.IsPlaying() { g.local.Side = g.Engines[g.local.Engine.Id()].Side g.local.Gid = g.Engines[g.local.Engine.Id()].PlayerGid g.local.Data = g.Engines[g.local.Engine.Id()] } var room Room err := base.LoadJson(filepath.Join(base.GetDataDir(), "rooms/basic.json"), &room) if err != nil { base.Error().Fatalf("%v", err) } errs := room.Validate() for _, err := range errs { base.Error().Printf("%v", err) } if len(errs) > 0 { base.Error().Fatalf("Errors with the level, bailing...") } g.Level = &Level{} g.Level.Room = room g.Rng = cmwc.MakeGoodCmwc() g.Rng.Seed(u.Seed) g.Ents = make(map[Gid]Ent) g.Friction = 0.97 g.losCache = makeLosCache(g.Level.Room.Dx, g.Level.Room.Dy) sides := make(map[int][]int64) var playerDatas []*PlayerData base.DoOrdered(g.Engines, func(a, b int64) bool { return a < b }, func(id int64, data *PlayerData) { sides[data.Side] = append(sides[data.Side], id) playerDatas = append(playerDatas, data) }) for id, ed := range g.Engines { base.Log().Printf("%v -> %v", id, *ed) } g.AddPlayers(playerDatas) g.MakeControlPoints() g.Init() base.Log().Printf("Nillifying g.Setup()") g.Setup = nil }
func (g *Game) ThinkGame() { // cache wall data if g.local.temp.AllWalls == nil || g.local.temp.AllWallsDirty { g.local.temp.AllWallsDirty = false g.local.temp.AllWalls = nil g.local.temp.WallCache = nil g.local.temp.VisibleWallCache = nil // Can't use a nil slice, otherwise we'll run this block every Think for levels // with no walls. allWalls := make([]linear.Seg2, 0) base.DoOrdered(g.Level.Room.Walls, func(a, b string) bool { return a < b }, func(_ string, walls linear.Poly) { for i := range walls { allWalls = append(allWalls, walls.Seg(i)) } }) g.local.temp.AllWalls = allWalls g.local.temp.WallCache = &wallCache{} g.local.temp.WallCache.SetWalls(g.Level.Room.Dx, g.Level.Room.Dy, allWalls, 100) g.local.temp.VisibleWallCache = &wallCache{} g.local.temp.VisibleWallCache.SetWalls(g.Level.Room.Dx, g.Level.Room.Dy, allWalls, stats.LosPlayerHorizon) g.local.pathingData = makePathingData(&g.Level.Room) } // cache ent data for _, ent := range g.local.temp.AllEnts { if ent.Dead() { if _, ok := ent.(*PlayerEnt); ok { var id int64 _, err := fmt.Sscanf(string(ent.Id()), "Engine:%d", &id) if err != nil { _, err = fmt.Sscanf(string(ent.Id()), "Ai:%d", &id) id = -id // Ai's engine ids are negative } if err != nil { base.Error().Printf("Unable to parse player id '%v'", ent.Id()) } else { if engineData, ok := g.Engines[id]; ok { if !ok { base.Error().Printf("Unable to find engine %d for player %v", id, ent.Id()) } else { engineData.CountdownFrames = 60 * 10 base.Log().Printf("%v died, counting down....", *engineData) } } } } ent.OnDeath(g) g.RemoveEnt(ent.Id()) } } // Death countdown base.DoOrdered(g.Engines, func(a, b int64) bool { return a < b }, func(_ int64, engineData *PlayerData) { if engineData.CountdownFrames > 0 { engineData.CountdownFrames-- if engineData.CountdownFrames == 0 { g.AddPlayers([]*PlayerData{engineData}) } } }) if g.local.temp.AllEnts == nil || g.local.temp.AllEntsDirty { g.local.temp.AllEnts = g.local.temp.AllEnts[0:0] g.DoForEnts(func(gid Gid, ent Ent) { g.local.temp.AllEnts = append(g.local.temp.AllEnts, ent) }) g.local.temp.AllEntsDirty = false } if g.local.temp.EntGrid == nil { g.local.temp.EntGrid = MakeEntCache(g.Level.Room.Dx, g.Level.Room.Dy) } g.local.temp.EntGrid.SetEnts(g.local.temp.AllEnts) for _, proc := range g.Processes { proc.Think(g) } algorithm.Choose(&g.Processes, func(proc Process) bool { return !proc.Dead() }) // Advance players, check for collisions, add segments eps := 1.0e-3 for _, ent := range g.local.temp.AllEnts { ent.Think(g) for _, ab := range ent.Abilities() { ab.Think(ent, g) } pos := ent.Pos() pos.X = clamp(pos.X, eps, float64(g.Level.Room.Dx)-eps) pos.Y = clamp(pos.Y, eps, float64(g.Level.Room.Dy)-eps) ent.SetPos(pos) } var nearby []Ent for i := 0; i < len(g.local.temp.AllEnts); i++ { outerEnt := g.local.temp.AllEnts[i] outerSize := outerEnt.Stats().Size() if outerSize == 0 { continue } g.local.temp.EntGrid.EntsInRange(outerEnt.Pos(), 100, &nearby) for _, innerEnt := range nearby { innerSize := innerEnt.Stats().Size() if innerSize == 0 { continue } distSq := outerEnt.Pos().Sub(innerEnt.Pos()).Mag2() colDist := innerSize + outerSize if distSq > colDist*colDist { continue } if distSq < 0.015625 { // this means that dist < 0.125 continue } dist := math.Sqrt(distSq) force := 50.0 * (colDist - dist) innerEnt.ApplyForce(innerEnt.Pos().Sub(outerEnt.Pos()).Scale(force / dist)) } } g.Level.ManaSource.Think(g.Ents) }