""" Objectives By Acid All Players + Player Compass + Laser arrows on ground + Vote Team Leader Team Leader + Set Objectives + Set Fireteams + Resign Team Leader """ import math import random import es import playerlib import popuplib import gamethread import effectlib import usermsg info = es.AddonInfo() info.name = "Objectives" info.version = "1.1b" info.author = "Acid" info.url = "http://www.taskforceranger.net" info.description = "Organize players into fireteams and place objective markers on map." info.basename = "Objectives" info.tags = "teamwork" # settings admin_start = False # can only admins can start objectives via rcon (obj_start and obj_end commands)? max_objectives = 4 # maximum number of objectives each team can have at one time min_players = 1 # players needed to start the plugin # possible objective colors for each team, chosen randomly. Be sure to have at least [max_objectives] entries in each one redteam_colors = {'Red': [255, 0, 0], 'Yellow': [255, 255, 0], 'Orange': [255, 140, 0], 'Purple': [153, 50, 205]} bluteam_colors = {'Blue': [0, 0, 255], 'Azure': [0, 255, 255], 'Green': [0, 255, 0], 'Magenta': [255, 0, 255]} votetime = 15 # seconds for voting for a TL # if your server is lagging, try increasing one or both of these hudupdatetime = 5 # how often to update the HUD overlay in seconds compassupdatetime = 0.1 # how often to re-draw the compass in seconds # possible objective names that populate the Add Objective menu. Users can specify their own names through the console command obj_names = ['Attack', 'Defend', 'Sentry', 'Sap', 'Cover', 'Snipe', 'Alpha', 'Bravo', 'Charlie', 'Delta', 'Echo'] # starting vars obj = [] #current objectives vote_red = {} vote_blu = {} red_tl = '0' blu_tl = '0' _i = 0 # unique id objectives_active = False mainmenu = None namemenu = None resignmenu = None def load(): global mainmenu, namemenu, resignmenu es.regsaycmd('$obj', 'Objectives/command', '') es.regsaycmd('$tl', 'Objectives/current_teamleader', '') # players "bind key +obj_compass" in console to use es.regclientcmd('+obj_compass', 'Objectives/compass_on', '') es.regclientcmd('-obj_compass', 'Objectives/compass_off', '') es.regclientcmd('obj', 'Objectives/console_command', '') es.regcmd('obj_start', 'Objectives/admin_start', '') es.regcmd('obj_end', 'Objectives/admin_end', '') #menus mainmenu = popuplib.easymenu('obj_main_menu', None, handlemenu) mainmenu.settitle('Objectives Menu') mainmenu.addoption('add', 'Add Objective') mainmenu.addoption('move', 'Move Objective') mainmenu.addoption('del', 'Remove Objective') mainmenu.addoption('players', 'Manage Players') mainmenu.addoption('resign', 'Resign') namemenu = popuplib.easymenu('obj_name_menu', None, handlename) namemenu.settitle('Objective Name') for name in obj_names: namemenu.addoption(name, name) resignmenu = popuplib.easymenu('obj_resign_menu', None, handleresign) resignmenu.settitle('Resign Options') resignmenu.addoption('elect', 'Select your replacement') resignmenu.addoption('vote', 'Let your team vote') objsay('Plugin Loaded') es_map_start({}) check_status() def unload(): global obj for o in obj: o.remove() es.unregsaycmd('$obj') popuplib.delete('obj_main_menu') popuplib.delete('obj_name_menu') popuplib.delete('obj_resign_menu') def es_map_start(ev): reset() def player_activate(ev): check_status() es.tell(ev['userid'], 'The Objectives plugin is enabled on this server.') es.tell(ev['userid'], 'To access the objectives compass, type "bind +obj_compass" in your console, then press .') def player_team(ev): global blu_tl, red_tl if check_status(): # module is active # check to see if player was a team leader if is_teamleader(ev['userid']): if str(ev['userid']) == str(blu_tl): blu_tl = '0' startvote_blu('function player_team, teamleader %s' % ev['userid']) elif str(ev['userid']) == str(red_tl): red_tl = '0' startvote_red('function player_team, teamleader %s' % ev['userid']) # remove user from all objectives for o in obj: o.del_player(ev['userid']) def player_disconnect(ev): global red_tl, blu_tl if check_status(): userid = str(ev['userid']) if is_teamleader(userid): if userid == red_tl: red_tl = '0' startvote_red('function player_disconnect, teamleader %s' % ev['userid']) elif userid == blu_tl: blu_tl = '0' startvote_blu('function player_disconnect, teamleader %s' % ev['userid']) for o in obj: o.del_player(userid) def admin_start(): start() def admin_end(): reset() def check_status(): global objectives_active if not objectives_active: # isn't already running if es.getplayercount() >= min_players: # start it start() elif es.getplayercount() < min_players: # now not enough players, unload # will still unload if in admin only mode when min players is reached objsay('Objectives mode disabled, not enough players.') reset() objectives_active = False return objectives_active def start(): global objectives_active objsay('Objectives mode starting.') gamethread.delayed(15, startvote, ('function start')) objectives_active = True hudupdate() def reset(): # reset variable structures global obj, vote_red, vote_blu, red_tl, blu_tl, objectives_active, _i vote_red = {} vote_blu = {} red_tl = '0' blu_tl = '0' _i = 0 objectives_active = False # kill existing objects/loops for o in obj: o.remove() obj = [] gamethread.cancelDelayed('hudloop') def startvote(reason = 'Other'): objsay('Initial Team Leader vote starting...') startvote_red(reason) startvote_blu(reason) def startvote_red(reason = 'Other'): global vote_red vote_red = {} objsay('Team Leader voting has started for the Red team (%s). Vote ends in %s seconds.' % (reason, votetime), '#t') votemenu_red = popuplib.easymenu('obj_vote_red', None, handlevote_red) votemenu_red.settitle('Vote for your Team Leader') votemenu_red.timeout('view', votetime) lr = playerlib.getPlayerList('#t') for u in lr: votemenu_red.addoption(int(u.userid), es.getplayername(u.userid)) for u in lr: votemenu_red.send(u.userid) gamethread.delayed(votetime, calculatevote_red, ()) def startvote_blu(reason = 'Other'): global vote_blu vote_blu = {} objsay('Team Leader voting has started for the Blu team (%s). Vote ends in %s seconds.' % (reason, votetime), '#ct') votemenu_blu = popuplib.easymenu('obj_vote_blu', None, handlevote_blu) votemenu_blu.settitle('Vote for your Team Leader') votemenu_blu.timeout('view', votetime) lb = playerlib.getPlayerList('#ct') for u in lb: votemenu_blu.addoption(int(u.userid), es.getplayername(u.userid)) for u in lb: votemenu_blu.send(u.userid) gamethread.delayed(votetime, calculatevote_blu, ()) def handlevote_red(userid, choice, popupname): global vote_red if choice in vote_red: vote_red[choice] = vote_red[choice] + 1 else: vote_red.update({choice: 1}) def handlevote_blu(userid, choice, popupname): global vote_blu if choice in vote_blu: vote_blu[choice] = vote_blu[choice] + 1 else: vote_blu.update({choice: 1}) def calculatevote_red(): global vote_red, red_tl # 2 = red = T # 3 = blu = CT red = playerlib.getPlayerList('#t') red_highest = 0 red_win = 0 if not (len(vote_red) == 0): for u in red: ui = u.userid if ui in vote_red: if vote_red[ui] > red_highest: red_highest = vote_red[ui] red_win = ui if not (red_highest == '0'): if not (str(es.getplayerteam(red_win)) == '2'): objsay('Red Team Leader is gone, vote restarting...', '#t') gamethread.delayed(5, startvote_red, ('function calculatevote_red, teamleader left team')) else: objsay('Red Team Leader is %s, you are required to follow orders from your Team Leader on this server.' % es.getplayername(red_win), '#t') red_tl = str(red_win) es.menu(30, red_tl, 'You have been voted Team Leader!\n \nTo access the Objectives menu,\ntype $obj in chat.\n \nPlease report errors to Acid.') else: objsay('No Team Leader voted upon. Vote restarting in 3 minutes.', '#t') gamethread.delayed(180, startvote_red, ('function calculatevote_red, no votes')) def calculatevote_blu(): global vote_blu, blu_tl # 2 = red = T # 3 = blu = CT blu = playerlib.getPlayerList('#ct') blu_highest = 0 blu_win = 0 if not (len(vote_blu) == 0): for u in blu: ui = u.userid if ui in vote_blu: if vote_blu[ui] > blu_highest: blu_highest = vote_blu[ui] blu_win = ui if not (blu_highest == 0): if not (str(es.getplayerteam(blu_win)) == '3'): objsay('Blu Team Leader is gone, vote restarting...', '#ct') gamethread.delayed(5, startvote_blu, ('function calculatevote_blu, teamleader left team')) else: objsay('Blu Team Leader is %s, you are required to follow orders from your Team Leader on this server.' % es.getplayername(blu_win), '#ct') blu_tl = str(blu_win) es.menu(30, blu_tl, 'You have been voted Team Leader!\n \nTo access the Objectives menu,\ntype $obj in chat.\n \nPlease report errors to Acid.') else: objsay('No Team Leader voted upon. Vote restarting in 3 minutes.', '#ct') gamethread.delayed(180, startvote_blu, ('function calculatevote_blu, no votes')) def objsay(txt, f = False): if not f: es.msg('#multi', '#green[ #lightgreenObj #green] ' + str(txt)) else: l = playerlib.getPlayerList(f) for u in l: es.tell(u.userid, txt) def is_teamleader(userid): if str(userid) in [blu_tl, red_tl]: return True else: return False def current_teamleader(): objsay('red_tl: %s, blu_tl: %s' % (red_tl, blu_tl)) def compass_on(): userid = es.getcmduserid() t = str(es.getplayerteam(userid)) if t == '2' or t == '3': if not playerlib.getPlayer(userid).attributes['isdead']: gamethread.delayed(0.0, draw_compass, (userid)) def draw_compass(userid): # do this first so there will almost always be a loop ready to cancel userid = str(userid) if playerlib.getPlayer(userid).attributes['isdead']: return gamethread.delayedname(compassupdatetime, 'compass%s' % userid, draw_compass, (userid)) if is_red(userid): r, g, b = (255, 0, 0) team = '2' else: r, g, b = (0, 0, 255) team = '3' x,y,z = es.getplayerlocation(userid) effectlib.drawCircle((x,y,z+30), 70, steps=24, seconds=compassupdatetime+0.1, width=1, endwidth=1, red=r, green=g, blue=b) if obj != []: for o in obj: if ((userid in o) or (is_teamleader(userid))) and o.team == team: #math time :) # we want to draw lines 70 units long on the xy axis # if my x is greater than objective x, compass laser points directly opposite of correct point vx = x - float(o.x) vy = y - float(o.y) vz = z - float(o.z) theta = math.atan(vy / vx) if vx < 0: theta = theta + math.pi # compensate for arctan's domain x = [-90,90] (1st and 4th quadrants) theta = theta - math.pi # now invert so angle is pointing at objective lx = 70 * math.cos(theta) ly = 70 * math.sin(theta) effectlib.drawLine((x,y,z+30), (lx+x,ly+y,z+30), seconds=compassupdatetime, width=2, endwidth=2, red=o.r, green=o.g, blue=o.b) def compass_off(): userid = es.getcmduserid() gamethread.cancelDelayed('compass%s' % userid) def command(): userid = es.getcmduserid() if is_teamleader(userid): mainmenu.send(userid) else: es.tell(userid, 'Not currently the Team Leader') def handlemenu(userid, choice, popupname): if not is_teamleader(userid): return if choice == 'add': if playerlib.getPlayer(userid).attributes['isdead']: es.tell(userid, 'You cannot use the Objective Add menu while dead.') return cur = 0 team = str(es.getplayerteam(userid)) for o in obj: if o.team == team: cur = cur + 1 if cur >= max_objectives: es.tell(userid, 'Maximum number of objectives already reached (%s)' % max_objectives) mainmenu.send(userid) return namemenu.send(userid) elif choice == 'del': team = es.getplayerteam(userid) delmenu = popuplib.easymenu('obj_del_menu_%s' % userid, None, handledel) delmenu.settitle('Delete Objective') for o in obj: if str(team) == str(o.team): delmenu.addoption(o.entname, '%s - %s member(s)' % (o.action, len(o))) delmenu.send(userid) elif choice == 'move': team = es.getplayerteam(userid) movemenu = popuplib.easymenu('obj_move_menu_%s' % userid, None, handlemove) movemenu.settitle('Move Objective') for o in obj: if str(team) == str(o.team): movemenu.addoption(o.entname, '%s - %s member(s)' % (o.action, len(o))) movemenu.send(userid) elif choice == 'players': if is_red(userid): t = playerlib.getPlayerList('#t') team = 2 else: t = playerlib.getPlayerList('#ct') team = 3 add_menu = popuplib.easymenu('obj_add_player_%s' % team, None, handleaddplayer) add_menu.settitle('Select Player') for u in t: ui = u.userid add_menu.addoption(ui, es.getplayername(ui)) add_menu.send(userid) elif choice == 'resign': resignmenu.send(userid) def handlename(userid, choice, popupname): global obj, _i if playerlib.getPlayer(userid).attributes['isdead']: return #now create the Objective object if is_red(userid): team = '2' else: team = '3' color = pickcolor(team) _i = _i + 1 p = playerlib.getPlayer(userid) # hack to get coords es.server.cmd('es_prop_dynamic_create %s %s' %(userid, 'props_c17/tv_monitor01_screen.mdl')) gamethread.delayed(0.5, handlename2, (userid, team, choice, color)) def pickcolor(teamid): if teamid == '2': color = random.choice(redteam_colors.values()) else: color = random.choice(bluteam_colors.values()) dead = False for o in obj: if o.team == teamid: if color == [o.r,o.g,o.b]: dead = True if dead: return pickcolor(teamid) else: return color def handlename2(userid, team, choice, color): global obj, _i index = int(es.ServerVar('eventscripts_lastgive')) coords = es.getindexprop(index, 'CBaseEntity.m_vecOrigin').split(',') x,y,z = coords px,py,pz = es.getplayerlocation(userid) es.server.cmd('es_remove %s' % index) effectlib.drawLine((px,py,pz), (x,y,z), seconds=2, red=255, green=255, blue=255, width=2) new = Objective('objective_%s' % _i, team, choice, coords, color) new.spawn() obj.append(new) mainmenu.send(userid) # Delete objective # Working def handledel(userid, choice, popupname): global obj for o in obj: if o.entname == choice: o.remove() mainmenu.send(userid) # Add player to objective # def handleaddplayer(userid, choice, popupname): global obj # now send a menu of current objective choices team = es.getplayerteam(userid) add2menu = popuplib.easymenu('obj_add2_player_%s' % userid, None, handleadd2player) add2menu.settitle('Add/Remove %s from' % es.getplayername(choice)) for o in obj: if str(team) == str(o.team): if choice in o: add2menu.addoption('%s,%s' % (choice, o.entname), '(Remove)%s-%s member(s)' % (o.action, len(o))) else: add2menu.addoption('%s,%s' % (choice, o.entname), '(Add)%s-%s member(s)' % (o.action, len(o))) add2menu.send(userid) # Add player to objective, second menu # def handleadd2player(userid, choice, popupname): global obj target,ent = choice.split(',') tteam = str(es.getplayerteam(target)) if is_red(userid) and tteam == '2': # target is red pass elif not is_red(userid) and tteam == '3': pass else: es.tell(userid, 'Invalid player target; player not on your team.') mainmenu.send(userid) return for o in obj: if o.entname == ent: if o.add_player(target): es.tell(target, 'You have been added to objective group %s' % o.action) es.tell(userid, '%s added to objective group %s' % (es.getplayername(target), o.action)) else: o.del_player(target) es.tell(userid, '%s removed from objective group %s' % (es.getplayername(target), o.action)) es.tell(target, 'You have been removed from objective group %s' % o.action) break mainmenu.send(userid) # move objective def handlemove(userid, choice, popupname): for o in obj: if choice == o.entname: o.move(userid) def handleresign(userid, choice, popupname): if choice == 'elect': if is_red(userid): team = '#t' else: team = '#ct' electmenu = popuplib.easymenu('obj_elect_%s' % team, None, handleelect) electmenu.settitle('Pick New Team Leader') t = playerlib.getPlayerList(team) for u in t: if str(u.userid) != str(userid): electmenu.addoption(u.userid, u.attributes['name']) electmenu.send(userid) elif choice == 'vote': if is_red(userid): startvote_red() else: startvote_blu() def handleelect(userid, choice, popupname): global blu_tl, red_tl choice = str(choice) tteam = str(es.getplayerteam(choice)) if is_red(userid) and tteam == '2': # target is red pass elif not is_red(userid) and tteam == '3': pass else: es.tell(userid, 'Invalid player target; player not on your team.') mainmenu.send(userid) return if is_red(choice): es.tell(userid, 'You have transferred power to %s' % es.getplayername(choice)) objsay('Red Team Leader is %s, you are required to follow orders from your Team Leader on this server.' % es.getplayername(choice), '#t') red_tl = str(choice) es.menu(30, red_tl, 'You have been voted Team Leader!\n \nTo access the Objectives menu,\ntype $obj in chat.\n \nPlease report errors to Acid.') else: es.tell(userid, 'You have transferred power to %s' % es.getplayername(choice)) objsay('Blu Team Leader is %s, you are required to follow orders from your Team Leader on this server.' % es.getplayername(choice), '#ct') blu_tl = str(choice) es.menu(30, blu_tl, 'You have been voted Team Leader!\n \nTo access the Objectives menu,\ntype $obj in chat.\n \nPlease report errors to Acid.') def anyplayer(): # returns a player id to spawn this as x = es.getUseridList('#alive') return x.pop() def is_red(userid): if str(es.getplayerteam(userid)) == '2': return True else: return False """ o = Objective('unique', 'Attack', es.getplayerteam(player), viewcoord, [color]) """ class Objective: def __init__(self, entname, team, action, coords, color): self.entname = entname self.team = str(team) self.action = action x,y,z = coords self.x = x self.y = y self.z = z self.coords = "%s %s %s" % (x,y,z) r,g,b = color self.color = "%s %s %s" % (r,g,b) self.r = r self.g = g self.b = b self.players = [] def spawn(self): self.index = es.createentity('env_smokestack', self.entname) es.spawnentity(self.index) val = {"BaseSpread" : 5, "SpreadSpeed" : 20, "WindSpeed" : 15, "InitialState" : 1, "Speed" : 5, "StartSize" : 3, "EndSize" : 7, "Rate" : 10, "JetLength" : 100, "twist" : 360, "SmokeMaterial" : "particle/SmokeStack.vmt", "rendercolor": self.color, "renderamt": 100, "origin": self.coords } p = anyplayer() for k in val.keys(): c = 'es_xfire %s %s addoutput "%s %s"' % (p, self.entname, k, val[k]) es.server.cmd(c) gamethread.delayed(1, es.server.cmd, 'es_fire %s %s TurnOn' % (p, self.entname)) def remove(self): global obj # check to see if index is still correct for i in es.createentitylist('env_smokestack'): if i == self.index: es.server.queuecmd('es_xremove %s' % self.index) obj.remove(self) def __contains__(self, player): return str(player) in self.players def __iter__(self): # return the iterator class return self.Iterator(self) def __len__(self): return len(self.players) def __str__(self): return '{entname: %s, index: %s, action: %s, origin: %s, color: %s, team: %s, players: %s}' % (self.entname, self.index, self.action, self.coords, self.color, self.team, self.players) def add_player(self, player): if str(player) in self.players: return False else: self.players.append(str(player)) return True def del_player(self, player): if str(player) in self.players: self.players.remove(str(player)) # move by re-creating def move(self, userid): es.server.cmd('es_prop_dynamic_create %s %s' %(userid, 'props_c17/tv_monitor01_screen.mdl')) gamethread.delayed(0.5, self.move2, (userid)) def move2(self, userid): global obj, _i _i = _i + 1 index = int(es.ServerVar('eventscripts_lastgive')) coords = es.getindexprop(index, 'CBaseEntity.m_vecOrigin').split(',') es.server.cmd('es_remove %s' % index) x,y,z = coords team = str(es.getplayerteam(userid)) color = (self.r,self.g,self.b) choice = self.action px,py,pz = es.getplayerlocation(userid) effectlib.drawLine((px,py,pz), (x,y,z), seconds=2, red=255, green=255, blue=255, width=2) new = Objective('objective_%s' % _i, team, choice, coords, color) new.spawn() obj.append(new) for p in self: new.add_player(p) es.tell(p, 'Objective %s has been moved.' % self.action) self.remove() es.tell(userid, 'Objective %s has been moved.' % self.action) class Iterator: def __init__(self, parent): self.loop = -1 self.len = len(parent.players) - 1 self.o = parent def next(self): if self.loop == self.len: raise StopIteration else: self.loop = self.loop + 1 return self.o.players[self.loop] # HUD hint loop def getcolor(color): if color in redteam_colors.values(): for k,v in redteam_colors.items(): if color == v: return k elif color in bluteam_colors.values(): for k,v in bluteam_colors.items(): if color == v: return k else: return 'Unknown' # can only send 255 chars, shorten wherever possible def hudupdate(): format = 'Leader:%s, Objectives\n--' if str(red_tl) != '0': format_red = format % (es.getplayername(red_tl)[:12]) else: format_red = format % ('(None)') if str(blu_tl) != '0': format_blu = format % (es.getplayername(blu_tl)[:12]) else: format_blu = format % ('(None)') pl = es.getUseridList('#alive') for p in pl: p = str(p) team = str(es.getplayerteam(p)) if team == '2': msg = format_red else: msg = format_blu for o in obj: if (is_teamleader(p) and team == str(o.team)): msg = msg + '\n%s(%s):' % (o.action, getcolor([o.r,o.g,o.b])) for p1 in o: msg = msg + es.getplayername(p1)[:10] + ',' elif p in o: msg = msg + '\n%s(%s):' % (o.action, getcolor([o.r,o.g,o.b])) for p1 in o: msg = msg + es.getplayername(p1)[:10] + ',' es.usermsg("create", p, "HintText") es.usermsg("write", "string", p, msg) es.usermsg("send", p, p, 0) es.usermsg("delete", p) gamethread.delayedname(hudupdatetime, 'hudloop', hudupdate, ()) # direct access for binding from console command # obj [add|del|move] [obj_name] def console_command(): userid = str(es.getcmduserid()) if not is_teamleader(userid): es.tell(userid, 'You are not the team leader.') return if playerlib.getPlayer(userid).attributes['isdead']: es.tell(userid, 'You cannot use this command while dead.') return if is_red(userid): team = '2' else: team = '3' subcmd = es.getargv(1).lower().strip() arg1 = es.getargv(2).strip() es.tell(userid, 'obj %s %s' % (subcmd, arg1)) if subcmd == 'add': if arg1 != '': handlename(userid, arg1, '') else: es.tell(userid, 'Syntax: obj add [objective_name]') elif subcmd == 'del': if arg1 != '': for o in obj: if o.team == team: if o.action == arg1: for p in [o, userid]: es.tell(p, 'Objective %s has been removed' % o.action) o.remove() else: es.tell(userid, 'Syntax: obj del [objective_name]') elif subcmd == 'move': if arg1 != '': for o in obj: if o.team == team: if o.action == arg1: o.move(userid) else: es.tell(userid, 'Syntax: obj move [objective_name]') else: es.tell(userid, 'Incorrect syntax. Possible commands:') es.tell(userid, 'obj [add|del|move] [objective_name]')