class World(DirectObject): #subclassing here is necessary to accept events def __init__(self): # Marc's stuff #turn off mouse control, otherwise camera is not repositionable base.disableMouse() #camera.setPosHpr(0, -15, 7, 0, -15, 0) #self.update() self.setupLights() render.setShaderAuto() #you probably want to use this ##self.cfont = loader.loadFont('Coalition_v2.ttf') self.wave_size = 3 self.max_enemies = 6 self.score = 0 self.wave = 0 self.background_music = loader.loadSfx("sounds/bgm.wav") self.shotgun_fire = loader.loadSfx("sounds/shotgun_fire.wav") self.smg_fire = loader.loadSfx("sounds/smg_fire.wav") self.mortar_fire = loader.loadSfx("sounds/mortar_fire.wav") self.explosion_1 = loader.loadSfx("sounds/explosion_1.wav") self.explosion_2 = loader.loadSfx("sounds/explosion_2.wav") self.hit = loader.loadSfx("sounds/bullet_hit.wav") self.shotgun_load = loader.loadSfx("sounds/shotgun_load.wav") self.smg_load = loader.loadSfx("sounds/smg_load.wav") self.mortar_load = loader.loadSfx("sounds/mortar_load.wav") self.background_music.setLoop(True) # Mapping some keys self.keyMap = {"left":0, "right":0, "forward":0, "back":0, "shoot":0, "rot_left":0, "rot_right":0} self.accept("escape", self.pause) self.accept("space", self.setKey, ["shoot", 1]) self.accept("space-up",self.setKey, ["shoot", 0]) self.accept("e", self.create_explosion) self.accept("r", self.reload) self.accept("l", self.toggle_light) self.accept("1", self.setSMG) self.accept("2", self.setShotgun) self.accept("3", self.setMortar) self.accept("w", self.setKey, ["forward", 1]) self.accept("d", self.setKey, ["right", 1]) self.accept("a", self.setKey, ["left", 1]) self.accept("s", self.setKey, ["back",1]) self.accept("arrow_left", self.setKey, ["rot_left",1]) self.accept("arrow_left-up", self.setKey, ["rot_left",0]) self.accept("arrow_right", self.setKey, ["rot_right",1]) self.accept("arrow_right-up", self.setKey, ["rot_right", 0]) self.accept("mouse1", self.setKey, ["fire", True]) self.accept("w-up", self.setKey, ["forward", 0]) self.accept("d-up", self.setKey, ["right", 0]) self.accept("a-up", self.setKey, ["left", 0]) self.accept("s-up", self.setKey, ["back", 0]) self.accept("mouse1-up", self.setKey, ["fire", False]) # Empty lists to track stuff self.enemies = [] self.bullets = [] self.mortars = [] # Find the start position self.player_start = (0,0,20) # Make a player object self.player = player.Player(self) self.count = 0 #Load Environment self.environ = loader.loadModel("models/terrain") self.environ.reparentTo(render) self.environ.setScale(0.66) self.environ.setPos(0,0,0) # Setup the collision detection rays self.cTrav = CollisionTraverser() # Player Rays self.player_cgray = CollisionRay() self.player_cgray.setOrigin(0,0,100) self.player_cgray.setDirection(0,0,-1) self.player_cgcol = CollisionNode("player_gray") self.player_cgcol.addSolid(self.player_cgray) self.player_cgcol.setFromCollideMask(BitMask32.bit(0)) self.player_cgcol.setIntoCollideMask(BitMask32.allOff()) self.player_cgcolnp = self.player.actor.attachNewNode(self.player_cgcol) self.player_cghandler = CollisionHandlerQueue() self.cTrav.addCollider(self.player_cgcolnp, self.player_cghandler) # Ground Rays self.cgray=CollisionRay() self.cgray.setOrigin(0,0,100) self.cgray.setDirection(0,0,-1) self.cgcol = CollisionNode("cgray") self.cgcol.addSolid(self.cgray) self.cgcol.setFromCollideMask(BitMask32.bit(0)) self.cgcol.setIntoCollideMask(BitMask32.allOff()) self.cgcolnp = camera.attachNewNode(self.cgcol) self.cghandler = CollisionHandlerQueue() self.cTrav.addCollider(self.cgcolnp, self.cghandler) self.paused = False self.setAI() self.hud_weapon = OnscreenText(text = "WEAPON: "+ str(self.player.selected_weapon), pos = (0.75, -0.8), scale = 0.07, fg=(180,180,180,1), shadow = (0,0,0,1)) self.hud_health = OnscreenText(text = "HEALTH: "+ str(self.player.health), pos= (-0.9, -0.8), scale = 0.07, fg=(180,180,180,1), shadow=(0,0,0,1)) self.hud_ammo = OnscreenText(text = "AMMO: ", pos=(0.75, -0.9), scale=0.07, fg=(180,180,180,1), shadow=(0,0,0,1)) self.hud_wave = OnscreenText(text = "WAVE: "+str(self.wave), pos= (-0.9, -0.9), scale = 0.07, fg=(180,180,180,1), shadow=(0,0,0,1)) self.hud_score = OnscreenText(text = "SCORE: "+str(self.score),pos= (0, -0.9), scale = 0.07, fg=(180,180,180,1), shadow=(0,0,0,1)) # Set the enemy spawn points and frequenct of spawns self.wavetimer = 30 self.spawnlocs = [(-1,-30,0),(3,30,0),(-13,2,0),(13,0,0)]# self.spawnTask = taskMgr.doMethodLater(2,self.spawnEnemies,'spawnTask') #self.explosions_handler = explosions.Explosions_Manager() self.explosions_handler = explosions.Explosions_Manager() taskMgr.add(self.update, "update") taskMgr.add(self.player_shoot, "Shoot") self.background_music.setVolume(0.4) self.background_music.play() def create_explosion(self): self.explosions_handler.Small_Explosion(VBase3(0,0,3)) def setKey(self, key, value): self.keyMap[key] = value def setSMG(self): self.player.set_weapon("SMG") def setShotgun(self): self.player.set_weapon("SHOTGUN") def setMortar(self): self.player.set_weapon("MORTAR") def reload(self): self.player.reload(self) def toggle_light(self): self.player.toggle_light() def player_shoot(self, task): if not self.paused: if self.keyMap["shoot"]: self.player.fire(self) return Task.cont def setupLights(self): #ambient light self.ambientLight = AmbientLight("ambientLight") #four values, RGBA (alpha is largely irrelevent), value range is 0:1 self.ambientLight.setColor((.5, .5, .4, 1)) self.ambientLightNP = render.attachNewNode(self.ambientLight) #the nodepath that calls setLight is what gets illuminated by the light render.setLight(self.ambientLightNP) #call clearLight() to turn it off self.keyLight = DirectionalLight("keyLight") self.keyLight.setColor((.6,.6,.6, 1)) self.keyLightNP = render.attachNewNode(self.keyLight) self.keyLightNP.setHpr(0, -26, 0) render.setLight(self.keyLightNP) """ self.fillLight = DirectionalLight("fillLight") self.fillLight.setColor((.4,.4,.4, 1)) self.fillLightNP = render.attachNewNode(self.fillLight) self.fillLightNP.setHpr(30, 0, 0) render.setLight(self.fillLightNP) """ def pause(self): if not self.paused: self.paused = True self.pause_text = OnscreenText(text = "PAUSED", pos = (0, 0.5), scale = 0.1, fg=(180,180,180,1), shadow = (0,0,0,1)) self.resume_button = DirectButton(text = ("RESUME"), scale = 0.2, command = self.resume_game, pos=(0, 0, 0.0)) self.exit_button = DirectButton(text = ("EXIT"), scale = 0.2, command = self.exit_game, pos=(0, 0,-0.4)) def setAI(self): """ Set up The AI world""" # Create the world self.AIworld = AIWorld(render) # Add the AIworld updater taskMgr.add(self.AIUpdate, "AIUpdate") def spawnEnemies(self,task): print "Spawning Wave!" task.delayTime = self.wavetimer if not self.paused: if len(self.enemies) + self.wave_size <= self.max_enemies: self.wave += 1 random.seed() for i in range(self.wave_size): x = math.floor(random.uniform(0,6)) if x < 1: self.newEnemy = enemies.Enemy2(self,random.choice(self.spawnlocs),"Enemy-%d-%d"%(self.wave,i)) elif x < 4: self.newEnemy = enemies.Enemy1(self,random.choice(self.spawnlocs),"Enemy-%d-%d"%(self.wave,i)) else: self.newEnemy = enemies.Enemy3(self,random.choice(self.spawnlocs),"Enemy-%d-%d"%(self.wave,i)) self.enemies.append(self.newEnemy) self.AIworld.addAiChar(self.newEnemy.setupAI(self.player.actor)) return task.again def AIUpdate(self,task): """ Update the AIWorld """ if not self.paused: self.AIworld.update() for e in self.enemies: if e.health <= 0: e.die(self) self.enemies.remove(e) else: e.updateHeight(self) return Task.cont def resume_game(self): self.remove_pause_menu() self.paused = False def exit_game(self): self.remove_pause_menu() sys.exit() def remove_pause_menu(self): if self.pause_text: self.pause_text.removeNode() if self.resume_button: self.resume_button.removeNode() if self.exit_button: self.exit_button.removeNode() def update(self, task): self.hud_health.setText("HEALTH: " + str(self.player.health)) if str(self.player.selected_weapon) == "SMG": self.hud_weapon.setText("WEAPON: MAIN GUN") else: self.hud_weapon.setText("WEAPON: " + str(self.player.selected_weapon)) self.hud_wave.setText("WAVE: " + str(self.wave)) self.hud_score.setText("SCORE: " + str(self.score)) if self.player.health <= 25: self.hud_health.setFg((180,0,0,1)) else: self.hud_health.setFg((180,180,180,1)) if self.player.selected_weapon == "SMG": self.hud_ammo.setText("AMMO: " + str(self.player.smg_mag)) if self.player.smg_mag == 0 or self.player.smg_reloading: self.hud_ammo.setFg((180,0,0,1)) else: self.hud_ammo.setFg((180,180,180,1)) elif self.player.selected_weapon == "SHOTGUN": self.hud_weapon.setText("WEAPON: " + self.player.selected_weapon) if self.player.shotgun_mag == 0 or self.player.shotgun_reloading: self.hud_ammo.setFg((180,0,0,1)) else: self.hud_ammo.setFg((180,180,180,1)) self.hud_ammo.setText("AMMO: " + str(self.player.shotgun_mag)) else: self.hud_ammo.setText("LOADED") if self.player.mortar_loaded == False: self.hud_ammo.setFg((180,0,0,1)) else: self.hud_ammo.setFg((180,180,180,1)) if self.pause == True: print "PAUSED" if self.player.health <=0: self.game_end = True return task.cont
class Connect4: def __init__(self, p_base): self.base = p_base self.render = p_base.render # Keyboard inputs map self.keyMap = {"left": False, "right": False, "down": False, "drop": False} # Global parameters self.player = 1 self.speed = 15 self.movement_V = False self.movement_H = False self.axes_H = [-3.6, -2.4, -1.2, 0, 1.2, 2.4, 3.6] self.axes_V = [0.25, -1.0, -2.25, -3.5, -4.75, -6] self.column = 3 self.line = 5 self.quit_game_bool = False self.round = 0 # Initialization of audio coin self.audio_coin = self.base.loader.loadMusic("connect4/audio/coin.ogg") # Initialization of the table self.table = self.base.loader.loadModel("connect4/models/table") self.table.reparentTo(self.render) self.table.setScale(2, 2, 2) self.table.setHpr(90, 0, 0) self.table_anim_start = self.table.posInterval(3, Point3(0, 30, -8), startPos=Point3(0, 0, -8)) self.table_anim_end = self.table.posInterval(3, Point3(0, 0, -8), startPos=Point3(0, 30, -8)) # Initialization of the grid self.grid = self.base.loader.loadModel("connect4/models/grid") self.grid.reparentTo(self.render) self.grid.setColor(0.1, 0.2, 0.8, 1.0) self.grid.setHpr(90, 0, 0) self.grid.setScale(0.6, 0.6, 0.625) self.grid_anim_start = self.grid.posInterval(3, Point3(3.6, 30, -6), startPos=Point3(3.6, 30, 0)) self.grid_anim_end = self.grid.posInterval(3, Point3(3.6, 30, 0), startPos=Point3(3.6, 30, -6)) self.gridContent = np.zeros(6 * 7) # Initialization of the discs self.discs = [] self.nb_discs = 44 for i in range(0, self.nb_discs): disc = self.base.loader.loadModel("connect4/models/disc") disc.reparentTo(self.render) if i % 2 == 0: color_disc = Disc(i, disc, 1.0, 0.0, 0.0) else: color_disc = Disc(i, disc, 1.0, 1.0, 0.0) self.discs.append(color_disc) self.first_disc_anim = self.discs[self.round].disc.posInterval(3, Point3(0, 30, 1.5), startPos=Point3(0, 0, 8)) # Initialization of start sequences self.init_sequence = Parallel(self.table_anim_start, self.grid_anim_start, self.first_disc_anim, name="p_start") self.init_sequence.start() # Initialization of keys self.base.accept("arrow_left", self.updateKeyMap, ["left", True]) self.base.accept("arrow_left-up", self.updateKeyMap, ["left", False]) self.base.accept("arrow_right", self.updateKeyMap, ["right", True]) self.base.accept("arrow_right-up", self.updateKeyMap, ["right", False]) self.base.accept("arrow_down", self.updateKeyMap, ["down", True]) self.base.accept("arrow_down-up", self.updateKeyMap, ["down", False]) self.base.accept("space", self.updateKeyMap, ["drop", True]) self.base.accept("space-up", self.updateKeyMap, ["drop", False]) # Initialization of winning cases self.results = [] with open("connect4/csv/cases.csv") as csvfile: reader = csv.reader(csvfile, quoting=csv.QUOTE_NONNUMERIC) for row in reader: self.results.append(row) # Initialization of fonts self.font = self.base.loader.loadFont("connect4/font/Roboto-Medium.ttf") self.font.setPixelsPerUnit(60) # Initialization of the victory text self.text_victory = OnscreenText(text='', pos=(1.4, -0.8), scale=0.1) self.text_victory.setFg((0, 0, 0, 1)) self.text_victory.setBg((1, 1, 1, 0)) self.text_victory.setShadow((0.5, 0.5, 0.5, 1)) # Initialization of buttons self.load_game_button = DirectButton(text="Load", pos=(-1.5, 0, 0.75), frameSize=(-3, 3, -0.5, 1), scale=.1, text_scale=0.9, command=self.load_game) self.new_game_button = DirectButton(text="New game", pos=(-1.5, 0, 0.9), frameSize=(-3, 3, -0.5, 1), scale=.1, text_scale=0.9, command=self.new_game) self.button_changed = False self.save_game_button = DirectButton(text="Save", pos=(-1.5, 0, 0.6), frameSize=(-3, 3, -0.5, 1), scale=.1, text_scale=0.9, command=self.save_game) self.quit_game_button = DirectButton(text="Quit", pos=(-1.5, 0, -0.95), frameSize=(-3, 3, -0.5, 1), scale=.1, text_scale=0.9, command=self.quit_game) self.hand_control_button = DirectButton(text="Activer le contrôle \n visuel", pos=(1.5, 0, -0.9), frameSize=(-3, 3, -1, 0.8), scale=.1, text_scale=0.5, command=self.activate_hand_control) # Mode # (mode 0 : default mode) # (mode 1 : hand control mode) self.mode = 0 self.disc_caught = -1 self.disc_dropped = -1 # Initialization of the right hand self.right_hand = self.base.loader.loadModel("connect4/models/hand") self.right_hand.reparentTo(self.render) self.right_hand.setPos(3.6, -20, 0) self.right_hand.setColor(0.88, 0.67, 0.41, 1.0) self.right_hand.setHpr(90, -90, 0) self.right_hand.setScale(0.2, 0.2, 0.2) # self.left_hand = self.base.loader.loadModel("connect4/models/hand") # self.left_hand.reparentTo(self.render) # self.left_hand.setPos(-3.6, -20, 0) # self.left_hand.setColor(0.88, 0.67, 0.41, 1.0) # self.left_hand.setHpr(90, -90, 180) # self.left_hand.setScale(0.2, 0.2, 0.2) def activate_hand_control(self): if self.mode == 0: self.mode = 1 self.hand_control_button.setText("Désactiver le contrôle \n visuel") self.right_hand.setPos(3.6, 30, 0) self.new_game() # self.left_hand.setPos(-3.6, 20, 0) else: self.mode = 0 self.hand_control_button.setText("Activer le contrôle \n visuel") self.right_hand.setPos(3.6, -20, 0) self.new_game() # self.left_hand.setPos(-3.6, -20, 0) def updateKeyMap(self, key, state): """ Function that updates the input map """ self.keyMap[key] = state def load_game(self): """ Load game functions used for load game button """ print("Connect 4 > Load a game") f1 = open("connect4/safeguard/safeguard.txt", "r") last_line = f1.readlines()[-1] f1.close() last_line_list = last_line.split(',') k = 0 p = 1 round = 0 for i in range(0, 42): col = i % 7 line = i // 7 if last_line_list[i] == '1': self.discs[k].disc.setPos(self.axes_H[col], 30, self.axes_V[line]) k += 2 round += 1 elif last_line_list[i] == '2': self.discs[p].disc.setPos(self.axes_H[col], 30, self.axes_V[line]) p += 2 round += 1 self.round = round self.discs[self.round].disc.setPos(0, 30, 1.5) self.gridContent = [int(j) for j in last_line_list] def new_game(self): """ New game functions used for new game button """ print("Connect 4 > New game") self.gridContent = np.zeros(6 * 7) self.round = 0 self.text_victory.setText('') if self.mode == 0: for i in range(0, 42): self.discs[i].disc.setPos(100, 100, 100) self.discs[self.round].disc.setPos(0, 30, 1.5) elif self.mode == 1: pos_xr = [-8.4, -7.2, -6.0, -12, -10.8, -9.6, -8.4, -7.2, -6.0, -12, -10.8, -9.6, -8.4, -7.2, -6.0, -12, -10.8, -9.6, -8.4, -7.2, -6.0] pos_xy = [8.4, 7.2, 6.0, 12, 10.8, 9.6, 8.4, 7.2, 6.0, 12, 10.8, 9.6, 8.4, 7.2, 6.0, 12, 10.8, 9.6, 8.4, 7.2, 6.0] pos_z = [-6.4, -6.4, -6.4, -5.2, -5.2, -5.2, -5.2, -5.2, -5.2, -4.0, -4.0, -4.0, -4.0, -4.0, -4.0, -2.8, -2.8, -2.8, -2.8, -2.8, -2.8] n = 0 p = 0 for i in range(0, 42): if i % 2 == 0: self.discs[i].disc.setPos(pos_xr[n], 30, pos_z[n]) n += 1 else: self.discs[i].disc.setPos(pos_xy[p], 30, pos_z[p]) p += 1 def save_game(self): """ Save game functions used for save game button """ print("Connect 4 > Save the game") grid = [int(j) for j in self.gridContent] grid_content_str = ','.join([str(elem) for elem in grid]) f = open("connect4/safeguard/safeguard.txt", "a") f.write(grid_content_str + "\n") f.close() def quit_game(self): """ Quit game functions used for quit game button """ print("Connect 4 > Quit the game") for i in range(0, self.nb_discs): self.discs[i].disc.removeNode() self.grid.removeNode() self.table.removeNode() self.new_game_button.destroy() self.save_game_button.destroy() self.load_game_button.destroy() self.quit_game_button.destroy() self.quit_game_bool = True def check_victory(self): """ Function that check if there is a victory case @return 1 if red wins and 2 if yellow wins """ if self.mode == 0: num_disc = self.round elif self.mode == 1: num_disc = self.disc_dropped if num_disc % 2 == 0: disc_type = 1 else: disc_type = 2 self.gridContent[7 * self.line + self.column] = disc_type for i in range(69): for j in range(4): if self.results[i][j] == 7 * self.line + self.column: if (self.gridContent[int(self.results[i][0])] == disc_type) and ( self.gridContent[int(self.results[i][1])] == disc_type) and ( self.gridContent[int(self.results[i][2])] == disc_type) and ( self.gridContent[int(self.results[i][3])] == disc_type): return disc_type return 0 def mainloop(self): """ Main loop of the connect 4 game """ # If quit_button is clicked if self.quit_game_bool: return 0 # Get the clock dt = globalClock.getDt() # Change the button "New game" to "Restart" for the first round if self.round == 1 and self.button_changed == False: self.new_game_button["text"] = "Restart" self.button_changed = True print("Connect 4 > Main loop") # Default mode if self.mode == 0: # Get the position of the current disc pos = self.discs[self.round].disc.getPos() # Left click if self.keyMap["left"] and self.column != 0 and not self.movement_V: self.keyMap["left"] = False self.column -= 1 self.movement_H = True # Right click if self.keyMap["right"] and self.column != 6 and not self.movement_V: self.keyMap["right"] = False self.column += 1 self.movement_H = True # down clic if self.keyMap["down"] and self.gridContent[self.column] == 0 and not self.movement_V: # To have only one click self.keyMap["down"] = False # Find the final disc line line_fixed = 0 self.line = 5 while line_fixed == 0 and self.line >= 0: if self.gridContent[7 * self.line + self.column] != 0: self.line -= 1 else: line_fixed = 1 self.movement_V = True # check if there is a victory or not victory = self.check_victory() if victory == 1: self.text_victory.setText('Red wins') if victory == 2: self.text_victory.setText('Yellow wins') # Progressive vertical movement if self.movement_V and pos.z >= self.axes_V[self.line]: pos.z -= self.speed * dt self.discs[self.round].disc.setPos(pos) # Set the disc position / Prepare next disc if self.movement_V and pos.z <= self.axes_V[self.line]: pos.z = self.axes_V[self.line] self.discs[self.round].disc.setPos(pos) self.audio_coin.play() self.movement_V = False self.line = 0 self.column = 3 self.round += 1 if self.round < 42 and self.mode == 0: self.discs[self.round].disc.setPos(0, 30, 1.5) # Horizontal movement if self.mode == 0 and self.movement_H: pos.x = self.axes_H[self.column] self.discs[self.round].disc.setPos(pos) self.movement_H = False # Handplay mode if self.mode == 1: # Detect hand position if cap.isOpened(): success, image = cap.read() image = cv2.cvtColor(cv2.flip(image, 1), cv2.COLOR_BGR2RGB) image.flags.writeable = False results = hands.process(image) # Draw the hand annotations on the image. image.flags.writeable = True image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR) # If a hand is detected if results.multi_hand_landmarks: for hand_landmarks in results.multi_hand_landmarks: for idx, landmark in enumerate(hand_landmarks.landmark): if idx == 9: x = 24 * landmark.x - 12 z = - 14 * landmark.y + 7 self.right_hand.setPos(x, 30, z) # If a is caught if self.disc_caught != -1: self.discs[self.disc_caught].disc.setPos(self.right_hand.getPos().x - 0.5, 30, self.right_hand.getPos().z + 0.5) else: for i in range(0, 42): if (abs(self.right_hand.getPos().x - 0.5 - self.discs[i].disc.getPos().x) < 0.5) \ and abs(self.right_hand.getPos().z - self.discs[i].disc.getPos().z) < 0.5: self.disc_caught = self.discs[i].id print("Connect 4 > Disc n°", self.disc_caught, " is caught") self.discs[self.disc_caught].disc.setPos(x - 0.5, 30, z + 0.5) # If space touch is pressed if self.keyMap["drop"]: print("Connect 4 > Disc n°", self.disc_caught, " is dropped") self.keyMap["drop"] = False pos_x = self.discs[self.disc_caught].disc.getPos().x min = 10 for i in range(len(self.axes_H)): if abs(self.axes_H[i] - pos_x) < min: self.column = i min = abs(self.axes_H[i] - pos_x) # Find the final disc line line_fixed = 0 self.line = 5 while line_fixed == 0 and self.line >= 0: if self.gridContent[7 * self.line + self.column] != 0: self.line -= 1 else: line_fixed = 1 self.movement_V = True self.discs[self.disc_caught].disc.setPos(self.axes_H[self.column], 30, self.axes_V[0]) self.disc_dropped = self.disc_caught self.disc_caught = -1 # check if there is a victory or not victory = self.check_victory() if victory == 1: self.text_victory.setText('Red wins') if victory == 2: self.text_victory.setText('Yellow wins') # Progressive vertical movement pos = self.discs[self.disc_dropped].disc.getPos() if self.movement_V and pos.z >= self.axes_V[self.line]: pos.z -= self.speed * dt self.discs[self.disc_dropped].disc.setPos(pos) # Set the disc position if self.movement_V and pos.z <= self.axes_V[self.line]: pos.z = self.axes_V[self.line] self.discs[self.disc_dropped].disc.setPos(pos) self.audio_coin.play() self.movement_V = False self.line = 0 self.column = 3 self.round += 1 return 1
class Console(FSM.FSM): def __init__(self,print_messenger_events=False): FSM.FSM.__init__(self,'console.fsm') self.defaultTransitions={'Closed':['Open'],'Open':['Closed']} self.lines=[] # self.textObject=OnscreenText(text='',pos=(-1.3,.95),scale=0.06,align=TextNode.ALeft) self.textObject.setFg((.95,.95,.95,.6)) self.textObject.setBg((.50,.50,.50,.5)) self.max_rows=15 #when true, prints panda messenger events traffic self.print_messenger=print_messenger_events self.out('console ready.') def enterClosed(self): self.accept(ConfigVariableString('console-trigger').getValue(),self.demand,extraArgs=['Open']) self.textObject.textNode.setOverallHidden(True) def enterOpen(self): self.accept(ConfigVariableString('console-trigger').getValue(),self.demand,extraArgs=['Closed']) self.textObject.textNode.setOverallHidden(False) def exitClosed(self): self.ignoreAll() def exitOpen(self): self.ignoreAll() def out(self,*args,**kwargs): line=[] if len(args)>0: line.extend([str(a)+' ' for a in args]) if len(kwargs.keys())>0: line.extend([str(kwargs)]) line=''.join(line) print line self.lines.append(line) self.lines=self.lines[-self.max_rows:] self.textObject.setText('\n'.join(self.lines)) def get_print_messenger(self): return self._print_messenger def set_print_messenger(self,b): ''' defines whether or not the console shows panda's messenger output. ''' self._print_messenger=b if b and messenger.quiet: messenger.toggleVerbose() self.task_watch_messenger=taskMgr.add(self.watch_messenger,'Console.watch_messenger') else: messenger.toggleVerbose() taskMgr.remove(self.task_watch_messenger) def watch_messenger(self,task): ''' prints out panda messenger's events traffic ''' self.out(str(messenger)) messenger.clear() return task.again if self._print_messenger else task.done print_messenger=property(get_print_messenger,set_print_messenger)
class ToontownLevelEditor(ShowBase): notify = directNotify.newCategory("Open Level Editor") APP_VERSION = open('ver', 'r').read() def __init__(self): # Load the prc file prior to launching showbase in order # to have it affect window related stuff loadPrcFile('editor.prc') builtins.userfiles = self.config.GetString('userfiles-directory') if not os.path.exists(userfiles): pathlib.Path(userfiles).mkdir(parents=True, exist_ok=True) # Check for -e or -d launch options parser = argparse.ArgumentParser(description="Modes") parser.add_argument("--experimental", action='store_true', help="Enables experimental features") parser.add_argument("--debug", action='store_true', help="Enables debugging features") parser.add_argument("--noupdate", action='store_true', help="Disables Auto Updating") parser.add_argument( "--compiler", nargs="*", help= "Specify which compiler to use (Only useful if your game uses a form of " "libpandadna.) Valid options are 'libpandadna', for games which use the " "modern c++ version of libpandadna (like Toontown Offline), and 'clash', " "for games that use the legacy python version of libpandadna, mainly Corporate Clash" ) parser.add_argument( "--server", nargs="*", help="Enables features exclusive to various Toontown projects", default='online') parser.add_argument( "--holiday", nargs="*", help="Enables holiday modes. [halloween or winter]") parser.add_argument( "--hoods", nargs="*", help="Only loads the storage files of the specified hoods", default=[ 'TT', 'DD', 'BR', 'DG', 'DL', 'MM', 'GS', 'GZ', 'SBHQ', 'LBHQ', 'CBHQ', 'BBHQ', 'OZ', 'PA', 'ES', 'TUT' ]) parser.add_argument( "dnaPath", nargs="?", help="Load the DNA file through the specified path") args = parser.parse_args() if args.experimental: loadPrcFileData("", "want-experimental true") if args.debug: loadPrcFileData("", "want-debug true") if args.compiler: loadPrcFileData("", f"compiler {args.compiler[0]}") if args.holiday: loadPrcFileData("", f"holiday {args.holiday[0]}") server = SERVER_TO_ID.get(args.server[0].lower(), DEFAULT_SERVER) self.server = server self.hoods = args.hoods # HACK: Check for dnaPath in args.hoods for hood in self.hoods[:]: if hood.endswith('.dna'): args.dnaPath = hood args.hoods.remove(hood) break # Check for any files we need and such self.__checkForFiles() # Import the main dlls so we don't have to repeatedly import them everywhere self.__importMainLibs() # Setup the root for Tkinter! self.__createTk() if not args.noupdate: loop = asyncio.get_event_loop() loop.run_until_complete(self.__checkUpdates()) self.__addCullBins() # Now we actually start the editor ShowBase.__init__(self) aspect2d.setAntialias(AntialiasAttrib.MAuto) # Create the framerate meter flag = self.config.GetBool('show-frame-rate-meter', False) if flag: self.toggleFrameRateMeter(flag) from toontown.leveleditor import LevelEditor self.le = LevelEditor.LevelEditor() self.le.startUp(args.dnaPath) def setFrameRateMeter(self, flag): return def toggleFrameRateMeter(self, flag): if flag: if not self.frameRateMeter: self.frameRateMeter = OnscreenText( parent=base.a2dTopRight, text='', pos=(-0.1125, -0.05, 0.0), scale=0.05, style=3, bg=(0, 0, 0, 0.4), font=ToontownGlobals.getToonFont()) taskMgr.add(self.frameRateCounter, 'fps') else: if self.frameRateMeter: self.frameRateMeter.destroy() self.frameRateMeter = None def frameRateCounter(self, task): """ Base code inspired from https://discourse.panda3d.org/t/trying-to-create-custom-fps-counter/25328/15 """ fps = globalClock.getAverageFrameRate() # Color is green by default color = (0, 0.9, 0, 1) # At or below 45 fps is yellow if fps <= 45: color = (1, 0.9, 0, 1) # At or below 30 fps is red elif fps <= 30: color = (1, 0, 0, 1) text = f'{round(fps, 1)} FPS' self.frameRateMeter.setText(text) self.frameRateMeter.setFg(color) return task.cont def __checkForFiles(self): # Make custom hood directory if it doesn't exist if not os.path.exists(f'{userfiles}/hoods/'): os.mkdir(f'{userfiles}/hoods/') # Make a maps directory if we don't have one if not os.path.isdir("maps"): os.mkdir("maps") # Make a Screenshots directory if we don't have one if not os.path.isdir("screenshots"): os.mkdir("screenshots") def __importMainLibs(self): builtin_dict = builtins.__dict__ builtin_dict.update( __import__('panda3d.core', fromlist=['*']).__dict__) builtin_dict.update(__import__('libotp', fromlist=['*']).__dict__) builtin_dict.update(__import__('libtoontown', fromlist=['*']).__dict__) def __createTk(self): tkroot = Tk() tkroot.withdraw() tkroot.title("Open Level Editor") if sys.platform == 'win32': # FIXME: This doesn't work in other platforms for some reason... tkroot.iconbitmap("resources/openttle_ico_temp.ico") self.tkRoot = tkroot def __addCullBins(self): cbm = CullBinManager.getGlobalPtr() cbm.addBin('ground', CullBinManager.BTUnsorted, 18) cbm.addBin('shadow', CullBinManager.BTBackToFront, 19) async def __checkUpdates(self): import aiohttp, webbrowser async with aiohttp.ClientSession() as session: try: async with session.get( "https://raw.githubusercontent.com/OpenToontownTools/OpenLevelEditor/master/ver" ) as resp: ver = await resp.text() ver = ver.splitlines()[0] if ver != self.APP_VERSION: self.notify.info( f"Client is out of date! Latest: {ver} | Client: {self.APP_VERSION}" ) if messagebox.askokcancel( "Error", f"Client is out of date!\nLatest: {ver} | Client: {self.APP_VERSION}. Press OK to be taken to the download page." ): webbrowser.open( "https://github.com/OpenToontownTools/OpenLevelEditor/releases/latest" ) else: self.notify.info("Client is up to date!") except: messagebox.showerror( message= "There was an error checking for updates! This is likely an issue with your connection. Press OK to continue using the application." )
class Life(ShowBase): def __init__(self): ShowBase.__init__(self) base.disableMouse() base.setFrameRateMeter(True) mydir = os.path.abspath(sys.path[0]) mydir = Filename.fromOsSpecific(mydir).getFullpath() self.bgmusic = self.loader.loadMusic(mydir + '/../sounds/bgmusic.ogg') self.bgmusic.play() # Setup collision for 3d picking self.picker = CollisionTraverser() self.pq = CollisionHandlerQueue() # Make a collision node for our picker ray self.pickerNode = CollisionNode('mouseRay') # Attach that node to the camera since the ray will need to be positioned # relative to it self.pickerNP = camera.attachNewNode(self.pickerNode) # Everything to be picked will use bit 1. This way if we were doing other # collision we could separate it self.pickerNode.setFromCollideMask(BitMask32.bit(1)) self.pickerRay = CollisionRay() self.pickerNode.addSolid(self.pickerRay) # Register the ray as something that can cause collisions self.picker.addCollider(self.pickerNP, self.pq) #self.picker.showCollisions(render) # Configure boxes and textures self.box = [[None for x in range(CELL_WIDTH)] for x in range(CELL_HEIGHT)] self.textureempty = self.loader.loadTexture(mydir + '/../textures/box.png') self.texturefull = self.loader.loadTexture(mydir + '/../textures/boxfull.png') self.textureempty.setMagfilter(Texture.FTLinear) self.textureempty.setMinfilter(Texture.FTLinearMipmapLinear) self.texturefull.setMagfilter(Texture.FTLinear) self.texturefull.setMinfilter(Texture.FTLinearMipmapLinear) self.worldnode = render.attachNewNode('worldnode') self.boxnode = self.worldnode.attachNewNode('boxnode') self.worldnode.setPos(0, 200, 0) for row in range(CELL_HEIGHT): for col in range(CELL_WIDTH): box = self.loader.loadModel(mydir + '/../models/cube') box.reparentTo(self.boxnode) box.setPos((CELL_WIDTH * -1) + (col * 2), 0, CELL_HEIGHT - (row * 2)) box.setTexture(self.textureempty) # Cube is the name of the polygon set in blender box.find("**/Cube").node().setIntoCollideMask(BitMask32.bit(1)) box.find("**/Cube").node().setTag('square', str(row) + '-' + str(col)) self.box[row][col] = box # Configure cell data self.cells = [[0 for x in range(CELL_WIDTH)] for x in range(CELL_HEIGHT)] self.cells[3][6] = 1 self.cells[4][7] = 1 self.cells[5][5] = 1 self.cells[5][6] = 1 self.cells[5][7] = 1 self.editmode = False taskMgr.add(self.start, 'start') # Setup initial event handling self.accept('escape', sys.exit) self.accept('enter', self.startgame) self.accept('mouse1', self.startgame) # Prep the main screen self.boxnode.setPosHpr(self.worldnode, -5, -160, 0, 60, -25, 0) self.readyText = OnscreenText(text='Life', pos=(0.91, 0.7), scale=0.2, fg=(255, 255, 255, 255), shadow=(0, 0, 0, 100)) def mouserotation(self, task): if not self.editmode and base.mouseWatcherNode.hasMouse(): self.boxnode.setH(self.worldnode, base.mouseWatcherNode.getMouseX() * 60) self.boxnode.setP(self.worldnode, -base.mouseWatcherNode.getMouseY() * 60) return task.cont def startgame(self): # Transition to the game start state taskMgr.add(self.transition, 'transition') interval = LerpPosHprInterval(self.boxnode, TRANSITIONPERIOD, Point3(0, 0, 0), Point3(0, 0, 0), other=self.worldnode, blendType='easeInOut') interval.start() def transition(self, task): self.ignore('enter') self.ignore('mouse1') self.readyText.setFg((255, 255, 255, (max(0, TRANSITIONPERIOD - task.time) / TRANSITIONPERIOD))) self.readyText.setShadow((0, 0, 0, (max(0, TRANSITIONPERIOD - task.time) / TRANSITIONPERIOD))) if task.time > TRANSITIONPERIOD: self.accept('enter', self.handleenter) self.accept('mouse1', self.selectpiece) taskMgr.add(self.mouserotation, 'mouserotation') return task.done else: return task.cont def selectpiece(self): if self.editmode: mpos = base.mouseWatcherNode.getMouse() self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY()) #Do the actual collision pass (Do it only on the squares for #efficiency purposes) self.picker.traverse(self.boxnode) if self.pq.getNumEntries() > 0: # If we have hit something, sort the hits so that the closest # is first, and highlight that node self.pq.sortEntries() tag = self.pq.getEntry(0).getIntoNode().getTag('square') tagsplit = tag.split('-') row = int(tagsplit[0]) col = int(tagsplit[1]) # Set the highlight on the picked square self.cells[row][col] = (0 if self.cells[row][col] == 1 else 1) def start(self, task): if not self.editmode: self.processcells(self.cells) for row in range(CELL_HEIGHT): for col in range(CELL_WIDTH): if self.cells[row][col] == 1: self.box[row][col].setTexture(self.texturefull) else: self.box[row][col].setTexture(self.textureempty) return task.cont def handleenter(self): self.editmode = not self.editmode @staticmethod def countsiblingcells(cells, x, y): return cells[y - 1][x - 1] + \ cells[y][x - 1] + \ cells[(y + 1) % CELL_HEIGHT][x - 1] + \ cells[y - 1][x] + \ cells[(y + 1) % CELL_HEIGHT][x] + \ cells[y - 1][(x + 1) % CELL_WIDTH] + \ cells[y][(x + 1) % CELL_WIDTH] + \ cells[(y + 1) % CELL_HEIGHT][(x + 1) % CELL_WIDTH] def processcells(self, cells): newcells = copy.deepcopy(cells) for row in range(CELL_HEIGHT): for col in range(CELL_WIDTH): neighbours = self.countsiblingcells(newcells, col, row) if newcells[row][col] == 1: if neighbours < 2: cells[row][col] = 0 elif 2 <= neighbours <= 3: pass elif neighbours > 3: cells[row][col] = 0 else: if neighbours == 3: cells[row][col] = 1
class Hood(StateData): def __init__(self, parentFSM, doneEvent, dnaStore, hoodId): StateData.__init__(self, doneEvent) self.parentFSM = parentFSM self.doneEvent = doneEvent self.dnaStore = dnaStore self.hoodId = hoodId self.id = None self.titleText = None self.suitFog = None self.suitLight = None self.suitLightColor = (0.4, 0.4, 0.4, 1) self.suitFogData = [(0.3, 0.3, 0.3), 0.0025] self.titleColor = (1, 1, 1, 1) return def enter(self, requestStatus): StateData.enter(self) hoodId = requestStatus['hoodId'] zoneId = requestStatus['zoneId'] rootZone = ZoneUtil.getZoneId(hoodId) if base.localAvatar.getLastHood( ) != rootZone and hoodId != CIGlobals.MinigameArea: base.localAvatar.b_setLastHood(rootZone) if not base.localAvatar.hasDiscoveredHood(rootZone): hoodsDiscovered = list(base.localAvatar.getHoodsDiscovered()) hoodsDiscovered.append(rootZone) base.localAvatar.b_setHoodsDiscovered(hoodsDiscovered) text = self.getHoodText(zoneId) self.titleText = OnscreenText(text, fg=self.titleColor, font=CIGlobals.getMickeyFont(), scale=0.15, pos=(0, -0.65)) self.titleText.hide() def enterTheLoader(self, requestStatus): self.fsm.request(requestStatus['loader'], [requestStatus]) def getHoodText(self, zoneId): if ZoneUtil.getWhereName(zoneId) == 'street' and zoneId < 61000: hoodText = CIGlobals.BranchZone2StreetName[ZoneUtil.getBranchZone( zoneId)] hoodText += '\n' + self.id else: hoodText = self.id if self.id != CIGlobals.MinigameArea: hoodText += '\n' + ZoneUtil.getWhereName(zoneId).upper() return hoodText def spawnTitleText(self, zoneId): hoodText = self.getHoodText(zoneId) self.doSpawnTitleText(hoodText) def doSpawnTitleText(self, hoodText): self.titleText.setText(hoodText) self.titleText.show() self.titleText.setColor(Vec4(*self.titleColor)) self.titleText.clearColorScale() self.titleText.setFg(self.titleColor) seq = Sequence( Wait(0.1), Wait(6.0), self.titleText.colorScaleInterval(0.5, Vec4(1.0, 1.0, 1.0, 0.0)), Func(self.titleText.hide)) seq.start() def hideTitleText(self): if self.titleText: self.titleText.hide() def exit(self): if self.titleText: self.titleText.cleanup() self.titleText = None StateData.exit(self) return def load(self): StateData.load(self) if self.storageDNAFile: loadDNAFile(self.dnaStore, self.storageDNAFile) if self.holidayDNAFile: loadDNAFile(self.dnaStore, self.holidayDNAFile) if not base.cr.holidayManager.getHoliday() == HolidayType.CHRISTMAS: self.createNormalSky() else: self.createSpookySky() def unload(self): self.notify.info("unload()") if hasattr(self, 'loader'): self.loader.exit() self.loader.unload() del self.loader del self.parentFSM del self.fsm self.dnaStore.reset_nodes() self.dnaStore.reset_hood_nodes() self.dnaStore.reset_place_nodes() self.dnaStore.reset_hood() self.dnaStore.reset_fonts() self.dnaStore.reset_DNA_vis_groups() self.dnaStore.reset_textures() self.dnaStore.reset_block_numbers() self.dnaStore.reset_block_zones() self.dnaStore.reset_suit_points() del self.dnaStore self.deleteCurrentSky() self.stopSuitEffect(0) self.ignoreAll() ModelPool.garbageCollect() TexturePool.garbageCollect() StateData.unload(self) def enterOff(self): pass def exitOff(self): pass def isSameHood(self, status): return status['hoodId'] == self.hoodId and status['shardId'] == None def enterQuietZone(self, requestStatus): base.transitions.noTransitions() loaderName = requestStatus['loader'] zoneID = requestStatus['zoneId'] where = requestStatus['where'] if where == 'playground' or where == 'toonInterior': name = self.id elif where == 'minigame': name = 'Minigame' elif where == 'street': name = CIGlobals.BranchZone2StreetName[ZoneUtil.getBranchZone( zoneID)] if loaderName == 'safeZoneLoader' or loaderName == 'townLoader': if not loader.inBulkBlock: loader.beginBulkLoad( 'hood', name, CIGlobals.safeZoneLSRanges.get(self.id, 6)) self.loadLoader(requestStatus) else: base.transitions.fadeScreen(1.0) self._quietZoneDoneEvent = uniqueName('quietZoneDone') self.acceptOnce(self._quietZoneDoneEvent, self.handleQuietZoneDone) self.quietZoneStateData = QuietZoneState(self._quietZoneDoneEvent) self.quietZoneStateData.load() self.quietZoneStateData.enter(requestStatus) def exitQuietZone(self): self.ignore(self._quietZoneDoneEvent) del self._quietZoneDoneEvent self.quietZoneStateData.exit() self.quietZoneStateData.unload() self.quietZoneStateData = None return def loadLoader(self, requestStatus): pass def handleQuietZoneDone(self): status = self.quietZoneStateData.getDoneStatus() loader.endBulkLoad('hood') self.fsm.request(status['loader'], [status]) if hasattr(self, 'loader'): self.loader.enterThePlace(status) def enterSafeZoneLoader(self, requestStatus): self.accept(self.loaderDoneEvent, self.handleSafeZoneLoaderDone) self.loader.enter(requestStatus) self.spawnTitleText(requestStatus['zoneId']) def exitSafeZoneLoader(self): self.ignore(self.loaderDoneEvent) self.hideTitleText() self.loader.exit() self.loader.unload() del self.loader def handleSafeZoneLoaderDone(self): doneStatus = self.loader.getDoneStatus() if self.isSameHood(doneStatus) or doneStatus['where'] == 'minigame': self.fsm.request('quietZone', [doneStatus]) else: self.doneStatus = doneStatus messenger.send(self.doneEvent) def createNormalSky(self): self.deleteCurrentSky() self.sky = loader.loadModel(self.skyFilename) if self.__class__.__name__ != 'CTHood': self.sky.setScale(1.0) self.sky.setFogOff() else: self.sky.setScale(5.0) def createSpookySky(self): self.deleteCurrentSky() self.sky = loader.loadModel(self.spookySkyFile) self.sky.setScale(5.0) self.sky.setFogOff() def deleteCurrentSky(self): if hasattr(self, 'sky'): if self.sky: self.sky.removeNode() del self.sky def startSuitEffect(self): self.stopSuitEffect() light = AmbientLight("suitLight") light.setColor(Vec4(*self.suitLightColor)) self.suitLight = render.attachNewNode(light) render.setLight(self.suitLight) self.suitFog = Fog("suitFog") self.suitFog.setColor(*self.suitFogData[0]) self.suitFog.setExpDensity(self.suitFogData[1]) render.setFog(self.suitFog) self.createSpookySky() Hood.startSky(self) def stopSuitEffect(self, newSky=1): render.clearFog() if self.suitLight: render.clearLight(self.suitLight) self.suitLight.removeNode() self.suitLight = None if self.suitFog: self.suitFog = None if newSky: if not base.cr.holidayManager.getHoliday( ) == HolidayType.CHRISTMAS: self.createNormalSky() else: self.createSpookySky() self.startSky() def startSky(self): self.sky.reparentTo(camera) self.sky.setZ(0.0) self.sky.setHpr(0.0, 0.0, 0.0) ce = CompassEffect.make(NodePath(), CompassEffect.PRot | CompassEffect.PZ) self.sky.node().setEffect(ce) def stopSky(self): self.sky.reparentTo(hidden)
class MyApp(ShowBase): gameMode = "Intro" def __init__(self): ShowBase.__init__(self) #self.cameraSetup() self.setupKeyboard() self.initIntro() def gameChangeMode(self): if base.gameMode == "Intro": self.initIntro() elif base.gameMode == "Convo": self.initConvo() def initIntro(self): self.fadeAlpha = 0.0 self.fadeTime = 30.0 self.fadeInc = 1.0 / self.fadeTime self.fadeDir = "up" self.sceneImage = OnscreenImage(image="images/scene.png", scale=(1920.0 / 1080.0, 1, 1)) self.sceneImage.setTransparency(TransparencyAttrib.MAlpha) self.sceneImage.setAlphaScale(self.fadeAlpha) self.imageNumber = 0 self.sceneImageList = ["images/scene.png", "images/image1.jpg"] #self.texty = OnscreenText(str(self.fadeAlpha)) taskMgr.add(self.gameIntroUpdate, "GameIntroUpdate") def gameIntroUpdate(self, task): self.slideManager() if (base.gameMode != "Intro"): self.gameChangeMode() return Task.done return Task.cont def cameraSetup(self): base.camLens.setAspectRatio(1920.0 / 1080.0) def slideManager(self): if self.fadeDir == "up" and self.fadeAlpha < 1: self.fadeAlpha += self.fadeInc self.sceneImage.setAlphaScale(self.fadeAlpha) #self.texty.setText(str(self.fadeAlpha)) if self.fadeDir == "down" and self.fadeAlpha > 0: self.fadeAlpha -= self.fadeInc self.sceneImage.setAlphaScale(self.fadeAlpha) if self.fadeDir == "up" and self.fadeAlpha >= 1: self.fadeDir = "down" if self.fadeDir == "down" and self.fadeAlpha <= 0: if self.imageNumber < 1: self.fadeDir = "up" self.sceneImage.setImage(self.nextImage()) self.sceneImage.setTransparency(TransparencyAttrib.MAlpha) self.sceneImage.setAlphaScale(self.fadeAlpha) else: self.fadeDir = "up" base.gameMode = "Convo" self.sceneImage.setImage(self.sceneImageList[self.imageNumber]) self.sceneImage.setTransparency(TransparencyAttrib.MAlpha) self.sceneImage.setAlphaScale(self.fadeAlpha) def nextImage(self): self.imageNumber += 1 return self.sceneImageList[self.imageNumber] def initConvo(self): self.textToType = "This will be used for talking\nThis is a good conversation box" self.textVisible = "" self.strOptionA = "Yes" self.strOptionB = "No" self.strOptionC = "Maybe" self.convoResponseSelected = False self.convoDepth = "1" self.intOptionCount = 3 #self.txtConvo = OnscreenText(self.textVisible, align = TextNode.ALeft) self.txtConvo = TextNode("Texty Bastard") self.txtConvo.setText(self.textToType) self.txtReply = TextNode("reply") self.txtReply.setText("") self.intHover = 0 #self.txtConvo.setFrameColor(0, 0, 1, 1) #self.txtConvo.setFrameAsMargin(0.2, 0.2, 0.1, 0.1) myFrame = DirectFrame(frameColor=(0.8, 0.8, 0.8, 0.4), frameSize=(-1, 0, -1, 0)) nodePathConvo = aspect2d.attachNewNode(self.txtConvo) nodePathConvo.setScale(0.07) nodePathConvo.setPos(-1, 0, -0.1) self.i = 0 self.indent = 0.0 self.selectedResponse = 0 self.txtOptionA = OnscreenText(text="", pos=(-1, -0.6), align=TextNode.ALeft) self.txtOptionB = OnscreenText(text="", pos=(-1, -0.7), align=TextNode.ALeft) self.txtOptionC = OnscreenText(text="", pos=(-1, -0.8), align=TextNode.ALeft) taskMgr.add(self.gameConvoUpdate, "GameConvoUpdate") def gameConvoUpdate(self, task): if (len(self.textVisible) != len(self.textToType)): task.delayTime = 0.001 while (self.i < len(self.textToType)): self.textVisible += self.textToType[self.i] self.txtConvo.setText(self.textVisible) self.i += 1 return Task.again else: taskMgr.add(self.gameConvoOptions, "ConvoOptions") return Task.done def gameConvoOptions(self, task): self.txtOptionA.setText(self.strOptionA) self.txtOptionB.setText(self.strOptionB) self.txtOptionC.setText(self.strOptionC) if self.convoResponseSelected == True: if self.convoCheckBranch("01", self.convoDepth): #self.txtConvo.setText(self.convoGetStrings("01", self.convoDepth)) self.convoNextDialogue("01", self.convoDepth) else: #self.txtConvo.setText("It DIDn't worked") self.convoDepth = "1" self.convoNextDialogue("01", self.convoDepth) return Task.done elif self.selectedResponse == 0: if self.intOptionCount > 0: self.txtOptionA.setX(-1 + self.indent) self.txtOptionA.setFg(fg=(1, 0, 0, 1)) if self.intOptionCount > 1: self.txtOptionB.setX(-1) self.txtOptionB.setFg(fg=(0, 0, 0, 1)) if self.intOptionCount > 2: self.txtOptionC.setX(-1) self.txtOptionC.setFg(fg=(0, 0, 0, 1)) self.indent = self.getIndent(self.indent, 0.01, 0.1) return Task.again elif self.selectedResponse == 1: if self.intOptionCount > 0: self.txtOptionA.setX(-1) self.txtOptionA.setFg(fg=(0, 0, 0, 1)) if self.intOptionCount > 1: self.txtOptionB.setX(-1 + self.indent) self.txtOptionB.setFg(fg=(1, 0, 0, 1)) if self.intOptionCount > 2: self.txtOptionC.setX(-1) self.txtOptionC.setFg(fg=(0, 0, 0, 1)) self.indent = self.getIndent(self.indent, 0.01, 0.1) return Task.again elif self.selectedResponse == 2: if self.intOptionCount > 0: self.txtOptionA.setX(-1) self.txtOptionA.setFg(fg=(0, 0, 0, 1)) if self.intOptionCount > 1: self.txtOptionB.setX(-1) self.txtOptionB.setFg(fg=(0, 0, 0, 1)) if self.intOptionCount > 2: self.txtOptionC.setX(-1 + self.indent) self.txtOptionC.setFg(fg=(1, 0, 0, 1)) self.indent = self.getIndent(self.indent, 0.01, 0.1) return Task.again def convoGetStrings(self, npcId, depth): if self.convoCheckBranch(npcId, depth) != True: return "#Error# String Not Found\nLooking for: " + "(" + str( npcId) + ")." + depth + ":" char = "" line = "" feed = "" path = os.path.abspath(os.getcwd()) f = open(path + "\\text\\stringsConvo.txt", "r") while line != "(" + str(npcId) + ")." + depth + ":": while char != ":": char = f.readline(1) line += char feed += "\n" + line if line != "(" + str(npcId) + ")." + depth + ":": char = "" line = "" f.readline() print(feed + " Selected") f.readline(1) line = f.readline() line = line.replace("##", "\n") f.close() return line def convoCheckBranch(self, npcId, depth): path = os.path.abspath(os.getcwd()) f = open(path + "\\text\\stringsConvo.txt", "r") char = "" line = "" branchFound = False while line != "<END>:": char = "" line = "" while char != ":": char = f.readline(1) line += char if line == "<END>:": f.close() return False elif line == "(" + str(npcId) + ")." + str(depth) + ":": f.close() return True else: f.readline() def convoNextDialogue(self, npcId, depth): self.textToType = self.convoGetStrings(npcId, depth) self.intOptionCount = self.convoGetOptionCount(npcId, depth) if self.intOptionCount == 1: self.strOptionA = self.convoGetStrings(npcId, depth + ".A") self.strOptionB = "" self.strOptionC = "" elif self.intOptionCount == 2: self.strOptionA = self.convoGetStrings(npcId, depth + ".A") self.strOptionB = self.convoGetStrings(npcId, depth + ".B") self.strOptionC = "" elif self.intOptionCount == 3: self.strOptionA = self.convoGetStrings(npcId, depth + ".A") self.strOptionB = self.convoGetStrings(npcId, depth + ".B") self.strOptionC = self.convoGetStrings(npcId, depth + ".C") else: self.strOptionA = "" self.strOptionB = "" self.strOptionC = "" self.textVisible = "" self.txtOptionA.setText("") self.txtOptionB.setText("") self.txtOptionC.setText("") self.txtConvo.setText(self.textVisible) #self.intOptionCount = 2 self.selectedResponse = 0 self.i = 0 self.convoResponseSelected = False taskMgr.add(self.gameConvoUpdate, "GameConvoUpdate") def convoGetOptionCount(self, npcId, depth): if self.convoCheckBranch( npcId, depth + ".A") and self.convoCheckBranch( npcId, depth + ".B") and self.convoCheckBranch( npcId, depth + ".C"): return 3 elif self.convoCheckBranch(npcId, depth + ".A") and self.convoCheckBranch( npcId, depth + ".B"): return 2 elif self.convoCheckBranch(npcId, depth + ".A"): return 1 else: return 0 def getIndent(self, value, increment, limit): if (value + increment >= limit): return limit else: return value + increment def setupKeyboard(self): self.accept("arrow_down", self.convoOptionDown) self.accept("arrow_up", self.convoOptionUp) self.accept("enter", self.convoOptionSelect) def convoOptionUp(self): self.indent = 0.0 if self.selectedResponse == 0: self.selectedResponse = self.intOptionCount - 1 elif self.selectedResponse > 0: self.selectedResponse -= 1 def convoOptionDown(self): self.indent = 0.0 if self.selectedResponse < self.intOptionCount - 1: self.selectedResponse += 1 elif self.selectedResponse == self.intOptionCount - 1: self.selectedResponse = 0 def convoOptionSelect(self): if self.selectedResponse == 0: self.convoDepth += ".A.1" elif self.selectedResponse == 1: self.convoDepth += ".B.1" elif self.selectedResponse == 2: self.convoDepth += ".C.1" self.convoResponseSelected = True
class Hood(StateData): def __init__(self, parentFSM, doneEvent, dnaStore, hoodId): StateData.__init__(self, doneEvent) self.parentFSM = parentFSM self.doneEvent = doneEvent self.dnaStore = dnaStore self.hoodId = hoodId self.abbr = "" self.id = None self.titleText = None self.suitFog = None self.suitLight = None self.suitLightColor = (0.4, 0.4, 0.4, 1) self.suitFogData = [(0.3, 0.3, 0.3), 0.0025] self.titleColor = (1, 1, 1, 1) self.wantLighting = True self.olc = ZoneUtil.getOutdoorLightingConfig(self.hoodId) return def makeLampLight(self, lamp): col = (255, 255, 255, 350) lightNP = CIGlobals.makePointLight( 'DLlamp', CIGlobals.colorFromRGBScalar255(col), lamp.getPos(render) + (0, 0, 9.5), 0.1) lamp.setLightOff(1) return lightNP def enter(self, requestStatus): StateData.enter(self) hoodId = requestStatus['hoodId'] zoneId = requestStatus['zoneId'] rootZone = ZoneUtil.getZoneId(hoodId) if base.localAvatar.getLastHood( ) != rootZone and hoodId != ZoneUtil.MinigameArea: base.localAvatar.b_setLastHood(rootZone) if not base.localAvatar.hasDiscoveredHood(rootZone): hoodsDiscovered = list(base.localAvatar.getHoodsDiscovered()) hoodsDiscovered.append(rootZone) base.localAvatar.b_setHoodsDiscovered(hoodsDiscovered) text = self.getHoodText(zoneId) self.titleText = OnscreenText(text, fg=self.titleColor, font=CIGlobals.getMickeyFont(), scale=0.15, pos=(0, -0.65)) self.titleText.hide() def enterTheLoader(self, requestStatus): self.fsm.request(requestStatus['loader'], [requestStatus]) def getHoodText(self, zoneId): if ZoneUtil.getWhereName(zoneId) == 'street' and zoneId < 61000: hoodText = ZoneUtil.BranchZone2StreetName[ZoneUtil.getBranchZone( zoneId)] hoodText += '\n' + self.id else: hoodText = self.id if self.id != ZoneUtil.MinigameArea: whereName = ZoneUtil.getWhereName(zoneId) if whereName == 'toonInterior': whereName = 'Unknown' try: whereName = ZoneUtil.zone2TitleDict.get(zoneId)[0] return whereName.upper() + '\n' + self.id except: pass hoodText += '\n' + whereName.upper() return hoodText def spawnTitleText(self, zoneId): hoodText = self.getHoodText(zoneId) self.doSpawnTitleText(hoodText) def doSpawnTitleText(self, hoodText): self.titleText.setText(hoodText) self.titleText.show() self.titleText.setColor(Vec4(*self.titleColor)) self.titleText.clearColorScale() self.titleText.setFg(self.titleColor) seq = Sequence( Wait(0.1), Wait(6.0), self.titleText.colorScaleInterval(0.5, Vec4(1.0, 1.0, 1.0, 0.0)), Func(self.hideTitleText)) seq.start() def hideTitleText(self): if self.titleText: self.titleText.hide() def exit(self): if self.titleText: self.titleText.cleanup() self.titleText = None StateData.exit(self) return def setupOutdoorLighting(self): self.olc.setup() def enableOutdoorLighting(self): self.olc.apply() def disableOutdoorLighting(self): self.olc.unapply() def cleanupOutdoorLighting(self): self.olc.cleanup() def load(self): StateData.load(self) if self.storageDNAFile: loadDNAFile(self.dnaStore, self.storageDNAFile) if self.holidayDNAFile: loadDNAFile(self.dnaStore, self.holidayDNAFile) self.setupOutdoorLighting() def unload(self): self.cleanupOutdoorLighting() if hasattr(self, 'loader'): self.loader.exit() self.loader.unload() del self.loader del self.parentFSM del self.fsm self.dnaStore.reset_nodes() self.dnaStore.reset_hood_nodes() self.dnaStore.reset_place_nodes() self.dnaStore.reset_hood() self.dnaStore.reset_fonts() self.dnaStore.reset_DNA_vis_groups() self.dnaStore.reset_materials() self.dnaStore.reset_block_numbers() self.dnaStore.reset_block_zones() self.dnaStore.reset_suit_points() del self.dnaStore self.ignoreAll() #CIGlobals.doSceneCleanup() StateData.unload(self) def enterOff(self): pass def exitOff(self): pass def isSameHood(self, status): return status['hoodId'] == self.hoodId and status['shardId'] is None def enterQuietZone(self, requestStatus): base.transitions.noTransitions() loaderName = requestStatus['loader'] zoneID = requestStatus['zoneId'] where = requestStatus['where'] if where == 'playground' or where == 'toonInterior': name = self.id elif where == 'minigame': name = 'Minigame' elif where == 'street': name = ZoneUtil.BranchZone2StreetName[ZoneUtil.getBranchZone( zoneID)] if loaderName == 'safeZoneLoader' or loaderName == 'townLoader': if not loader.inBulkBlock: loader.beginBulkLoad('hood', name, ZoneUtil.safeZoneLSRanges.get(self.id, 6)) self.loadLoader(requestStatus) else: base.transitions.fadeScreen(1.0) self._quietZoneDoneEvent = uniqueName('quietZoneDone') self.acceptOnce(self._quietZoneDoneEvent, self.handleQuietZoneDone) self.quietZoneStateData = QuietZoneState(self._quietZoneDoneEvent) self.quietZoneStateData.load() self.quietZoneStateData.enter(requestStatus) def exitQuietZone(self): self.ignore(self._quietZoneDoneEvent) del self._quietZoneDoneEvent self.quietZoneStateData.exit() self.quietZoneStateData.unload() self.quietZoneStateData = None return def loadLoader(self, requestStatus): pass def handleQuietZoneDone(self): status = self.quietZoneStateData.getDoneStatus() loader.endBulkLoad('hood') self.fsm.request(status['loader'], [status]) if hasattr(self, 'loader'): self.loader.enterThePlace(status) def enterSafeZoneLoader(self, requestStatus): self.accept(self.loaderDoneEvent, self.handleSafeZoneLoaderDone) self.loader.enter(requestStatus) self.spawnTitleText(requestStatus['zoneId']) def exitSafeZoneLoader(self): self.ignore(self.loaderDoneEvent) self.hideTitleText() self.loader.exit() self.loader.unload() del self.loader def handleSafeZoneLoaderDone(self): doneStatus = self.loader.getDoneStatus() if self.isSameHood(doneStatus) or doneStatus['where'] == 'minigame': self.fsm.request('quietZone', [doneStatus]) else: self.doneStatus = doneStatus messenger.send(self.doneEvent)
class ScoreScreen(GameScreen): def __init__(self, screen_manager): ''' Constructor ''' super(ScoreScreen, self).__init__(screen_manager, 'score_screen') self.player_text = OnscreenText( "Player 1", 1, font=base.fontLoader.load('digital.ttf'), fg=(1, 1, 1, 1), pos=(0, .93), align=TextNode.ACenter, scale=.1, mayChange=True, parent=self.node2d) self.score_text = OnscreenText("00", 1, font=base.fontLoader.load('arial.ttf'), fg=((0.0 / 255.0), (255.0 / 255.0), (255.0 / 255.0), 1), pos=(0, .8), align=TextNode.ACenter, scale=.15, mayChange=True, parent=self.node2d) self.ball_text = OnscreenText( "BALL 1", 1, font=base.fontLoader.load('motorwerk.ttf'), fg=(1, 1, 1, 1), pos=(1.15, 0.92), scale=.1, mayChange=True, parent=self.node2d) self.acmag_text = DMDSpriteFont("assets/sprites/num_font_dotmatrix", self.node2d, sX=0.2, sY=0.2, sZ=0.2) self.acmag_text.hide() self.acmag_text.setPos((1.0, 0, 0.7)) self.clock = OnscreenImage(image='assets/images/time.png', pos=(0.78, 0, 0.93), parent=self.node2d, scale=.06) self.bonus2x = OnscreenImage(image='assets/images/2x.png', pos=(-1.25, 0, 0.93), parent=self.node2d, scale=.06) self.bonus3x = OnscreenImage(image='assets/images/3x.png', pos=(-1.25, 0, 0.93), parent=self.node2d, scale=.06) self.bonus4x = OnscreenImage(image='assets/images/4x.png', pos=(-1.25, 0, 0.93), parent=self.node2d, scale=.06) self.bonus5x = OnscreenImage(image='assets/images/5x.png', pos=(-1.25, 0, 0.93), parent=self.node2d, scale=.06) self.cfb = OnscreenImage(image='assets/images/cfb.png', pos=(-1.1, 0, 0.93), parent=self.node2d, scale=.06) self.sj = OnscreenImage(image='assets/images/hud_superjets.png', pos=(-1.0, 0, 0.93), parent=self.node2d, scale=.06) self.sj_text = OnscreenText("25", 1, font=base.fontLoader.load('motorwerk.ttf'), fg=(1, 0, 0, 1), pos=(-0.5, 0.92), scale=.1, mayChange=True, parent=self.node2d) self.timer_text = OnscreenText( "--", 1, font=base.fontLoader.load('motorwerk.ttf'), fg=(1, 0, 0, 1), pos=(0.88, 0.92), scale=.1, mayChange=True, parent=self.node2d) modInfo = self.place_model(model_name="spinner.egg", scale=(0.1, 0.2, 0.2), pos=(-23, 120, -30), rotate=False, p=7, h=15, reference="spinner") self.random_award_text = OnscreenText( "", 1, font=base.fontLoader.load('eurostile.ttf'), fg=(0, 1, 0, 1), pos=(0, 0, 0), scale=.15, mayChange=True, parent=self.node2d) #modInfo['interval'] = modInfo['model'].hprInterval( 0.6, Vec3(15,450,0) ) #modInfo['interval'].loop() modInfo['interval'] = None self.spinner = modInfo self.laser_millions = OnscreenImage( parent=self.node2d, # Parented to our 2d node renderer image="assets/images/laser_millions.png", # File name specified pos=(0, 0, -2), # z: -2 is off screen at the bottom scale=(0.3, 0.3, 0.3) ) # Scale it down a bit horizontally and vertically to make it look right self.ef_bonus = OnscreenImage( parent=self.node2d, # Parented to our 2d node renderer image="assets/images/edgar_friendly.png", # File name specified pos=(0, 0, 0), # z: -2 is off screen at the bottom scale=(0.3, 0.3, 0.3) ) # Scale it down a bit horizontally and vertically to make it look right self.ef_bonus_task = None self.toggle_ef = False self.ef_bonus.hide() self.top_popper_award = OnscreenImage( parent=self.node2d, # Parented to our 2d node renderer image="assets/images/top_award_bg.png", # File name specified pos=(0, 0, 0), # z: -2 is off screen at the bottom scale=(0.75, 0.3, 0.3), sort=-20, ) # Scale it down a bit horizontally and vertically to make it look right self.top_popper_award.hide() self.standup_collected = OnscreenImage( parent=self.node2d, # Parented to our 2d node renderer image="assets/images/standup_spotted.png", # File name specified pos=(0, 0, -2), # z: -2 is off screen at the bottom scale=(0.6, 0.3, 0.4) ) # Scale it down a bit horizontally and vertically to make it look right self.mtlM = Sprite(parent=self.node, file_name="assets/sprites/mtl/m/m", file_type="png", num_frames=33, int_padding=4, scale=(2.5, 2.5, 2.5), pos=(6, 40, 15)) self.mtlT = Sprite(parent=self.node, file_name="assets/sprites/mtl/t/t", file_type="png", num_frames=33, int_padding=4, scale=(2.5, 2.5, 2.5), pos=(9, 40, 15)) self.mtlL = Sprite(parent=self.node, file_name="assets/sprites/mtl/l/l", file_type="png", num_frames=33, int_padding=4, scale=(2.5, 2.5, 2.5), pos=(12, 40, 15)) self.retina = Sprite(parent=self.node, file_name="assets/sprites/retina/retina_scan_", file_type="png", num_frames=36, int_padding=2, scale=(7.5, 7.5, 7.5), pos=(-10, 40, 0), auto_gc=False) self.hide_m() self.hide_t() self.hide_l() self.hide_retina() def set_award_text(self, text): self.random_award_text.setText(text) def show_top_award(self): self.top_popper_award.show() self.random_award_text.show() def hide_top_award(self): self.random_award_text.hide() self.top_popper_award.hide() def spin_spinner(self): blue_side = 3435 red_side = 3255 if random.randint(1, 2) == 1: angle = blue_side else: angle = red_side if self.spinner['interval'] != None: self.spinner['interval'].finish() self.spinner['interval'] = None self.spinner['model'].setP(0) self.spinner['interval'] = self.spinner['model'].hprInterval( 3, Vec3(15, angle, 0), blendType='easeOut') self.spinner['interval'].start() def update_acmag_text(self, text): self.acmag_text.setText(text) self.acmag_text.show() def hide_acmag_text(self): self.acmag_text.hide() def update_timer_text(self, text): self.timer_text.setText(text) def update_score(self, player, score): self.player_text.setText(str(player)) if score == 0: self.score_text.setText("00") else: #self.score_text.setText(str(locale.format("%d", score, grouping=True))) self.score_text.setText(str(base.format_score(score))) def update_ball(self, ball): self.ball_text.setText("BALL " + str(ball)) self.update_hud() def show_score(self): self.player_text.show() self.score_text.show() def hide_score(self): self.player_text.hide() self.score_text.hide() def hide_ball(self): self.ball_text.hide() def show_ball(self): self.ball_text.show() def hide_timer(self): self.timer_text.hide() self.clock.hide() def show_timer(self): self.timer_text.show() self.clock.show() def blink_score(self): i = Sequence( LerpColorScaleInterval(self.score_text, 0.5, VBase4(0, 0, 1, 1)), LerpColorScaleInterval(self.score_text, 0.5, VBase4(1, 1, 1, 1))) i.start() def set_score_color(self, color): self.score_text.setFg(color) def show_laser_millions(self): # Load our trunk bonus image, parent it to our 2d renderer # We must also enable transparency on the image otherwise we get big ugly black squares self.laser_millions.setTransparency(TransparencyAttrib.MAlpha) # Set up a sequence to perform the animation in, pause and out... "sequentially" s = Sequence( # Lerp stands for "linearly interpolate", so we move from one position to the other with # an 'easeOut' blend so it comes to a nice slow stop at the end instead of an abrupt finish LerpPosInterval(self.laser_millions, 0.7, pos=(-1, 0, 0.7), startPos=(-3, 0, 0.7), blendType='easeOut'), # Pause the sequence for 2.5 seconds Wait(2.5), # Animate back to our home position (off screen) with an ease in so it starts moving gradually LerpPosInterval(self.laser_millions, 0.7, pos=(-3, 0, 0.7), blendType='easeIn')) # Fire off the sequence s.start() def show_standup_collected(self): # Load our trunk bonus image, parent it to our 2d renderer # We must also enable transparency on the image otherwise we get big ugly black squares self.standup_collected.setTransparency(TransparencyAttrib.MAlpha) # Set up a sequence to perform the animation in, pause and out... "sequentially" s = Sequence( # Lerp stands for "linearly interpolate", so we move from one position to the other with # an 'easeOut' blend so it comes to a nice slow stop at the end instead of an abrupt finish LerpPosInterval(self.standup_collected, 0.7, pos=(0, 0, 0), startPos=(-3, 0, 0), blendType='easeOut'), # Pause the sequence for 2.5 seconds Wait(2.5), # Animate back to our home position (off screen) with an ease in so it starts moving gradually LerpPosInterval(self.standup_collected, 0.7, pos=(-3, 0, 0), blendType='easeIn')) # Fire off the sequence s.start() def show_ef_bonus(self): if self.ef_bonus_task != None: return self.ef_bonus.show() self.ef_bonus_task = base.taskMgr.doMethodLater( 0.01, self.toggle_ef_bonus, 'ef_bonus') def toggle_ef_bonus(self, task): self.toggle_ef = not self.toggle_ef if self.toggle_ef: self.ef_bonus.hide() else: self.ef_bonus.show() return task.again def hide_ef_bonus(self): if self.ef_bonus_task != None: base.taskMgr.remove(self.ef_bonus_task) self.ef_bonus.hide() self.ef_bonus_task = None def show_m(self): self.mtlM.show() def show_t(self): self.mtlT.show() def show_l(self): self.mtlL.show() def hide_m(self): self.mtlM.hide() def hide_t(self): self.mtlT.hide() def hide_l(self): self.mtlL.hide() def show_retina(self): self.retina.show(1) def hide_retina(self): self.retina.hide() def update_hud(self): self.bonus2x.hide() self.bonus3x.hide() self.bonus4x.hide() self.bonus5x.hide() self.cfb.hide() self.sj.hide() self.sj_text.hide() if base.hwgame.current_player().bonus_x == 2: self.bonus2x.show() if base.hwgame.current_player().bonus_x == 3: self.bonus3x.show() if base.hwgame.current_player().bonus_x == 4: self.bonus4x.show() if base.hwgame.current_player().bonus_x >= 5: self.bonus5x.show() if base.hwgame.current_player().call_for_backup: self.cfb.show() if base.hwgame.current_player().super_jets: self.sj.show() self.sj_text.setText( str(base.hwgame.current_player().super_jets_left))
class example_sounds(BaseNodeScript): ''' Gives an example of how to play and manage sounds. The code below runs as the managing script of the example_sounds node which is defined in example_sounds.node . Such managing scripts are termed NodeScript and are essentially an FSM linked with a node for the purpose of managing its state. Nodescripts are optional though, nodes can be defined without a nodescript. Being state machines in nature, you will methods such as 'enter', 'exit' and 'update'. The 'enter' method will be called only once, at the time the node becomes active. Likewise 'exit' will be called only once, when the node becomes inactive. On the other hand 'update' will be invoked with the game logic update, so many times per second. ''' def __init__(self, game, node): BaseNodeScript.__init__(self, game, 'SoundsExample', node) self.log = logging.getLogger('SoundsExample') # reference to sounds fx player self.sounds = None # the following set of fields are UI labels self.instructions = None self.soundsStatusText = None self.volumeText = None self.rateText = None self.balanceText = None self.loopText = None self.eventText = None # reference to the 'blah' sound self.blahSound = None # holds the status of sounds, if True then sounds are globally enabled self.lastStatus = True # flags that indicate if the respective property has been update by the user self.updateVolume = False self.updateRate = False self.updateBalance = False self.updateLooping = False # self.looping = 0 def registerMessages(self): ''' The purpose of this method is to declare the list of messages IDs for which we are interested to receive. Here we are interested in a single message besides the standard list of message IDs returned by the base class. It is not necessary to include the list of messages from the base class but it contains some very useful message IDs like game_paused, game_resumed, etc., so most probably you will want to do this. ''' return ['sound_finished'].extend(BaseNodeScript.registerMessages(self)) def enter(self): ''' Here we initialize some fields and flags and setup our UI which constists only of labels. ''' # call the super class implementation first, this is optional BaseNodeScript.enter(self) self.sounds = self.game.getSoundsFx() # hold the camera fixed self.game.getView().getCameraController().disable() # display instructions self.instructions = OnscreenText(text = 'Press 1 for blah-blah, 2 for cling and 3 for zipper', pos = (base.a2dLeft + 0.1, 0.9), align = TextNode.ALeft, scale = 0.07, fg = (1.0, 1.0, 1.0, 1.0), shadow = (0.0, 0.0, 0.0, 0.7) ) # gets the status of sounds self.lastStatus = self.sounds.isEnabled() self.soundsStatusText = OnscreenText(text = 'Sounds status: %s' % self.lastStatus, pos = (base.a2dLeft + 0.1, 0.8), align = TextNode.ALeft, scale = 0.07, fg = (1.0, 1.0, 1.0, 1.0), shadow = (0.0, 0.0, 0.0, 0.7), mayChange = True ) self.volumeText = OnscreenText(text = 'Volume: %.1f' % self.sounds.getVolume(), pos = (base.a2dLeft + 0.1, 0.7), align = TextNode.ALeft, scale = 0.07, fg = (1.0, 1.0, 1.0, 1.0), shadow = (0.0, 0.0, 0.0, 0.7), mayChange = True ) self.rateText = OnscreenText(text = 'Rate: %.1f' % self.sounds.getPlayRate(), pos = (base.a2dLeft + 0.1, 0.6), align = TextNode.ALeft, scale = 0.07, fg = (1.0, 1.0, 1.0, 1.0), shadow = (0.0, 0.0, 0.0, 0.7), mayChange = True ) self.balanceText = OnscreenText(text = 'Balance: %.1f' % self.sounds.getBalance(), pos = (base.a2dLeft + 0.1, 0.5), align = TextNode.ALeft, scale = 0.07, fg = (1.0, 1.0, 1.0, 1.0), shadow = (0.0, 0.0, 0.0, 0.7), mayChange = True ) self.loopText = OnscreenText(text = 'Looping: %d' % self.looping, pos = (base.a2dLeft + 0.1, 0.4), align = TextNode.ALeft, scale = 0.07, fg = (1.0, 1.0, 1.0, 1.0), shadow = (0.0, 0.0, 0.0, 0.7), mayChange = True ) self.eventText = OnscreenText(text = 'Finished event received', pos = (base.a2dLeft + 0.1, 0.3), align = TextNode.ALeft, scale = 0.07, fg = (1.0, 0.0, 0.0, 1.0), shadow = (0.0, 0.0, 0.0, 0.7), mayChange = True ) self.eventText.hide() OnscreenText(text = 'Press P, R to pause/resume playing sounds', pos = (base.a2dLeft + 0.1, 0.2), align = TextNode.ALeft, scale = 0.05, fg = (1.0, 1.0, 1.0, 1.0), shadow = (0.0, 0.0, 0.0, 0.7), mayChange = True ) OnscreenText(text = 'Press L, Shift-L to increase/decrease the loop count', pos = (base.a2dLeft + 0.1, 0.1), align = TextNode.ALeft, scale = 0.05, fg = (1.0, 1.0, 1.0, 1.0), shadow = (0.0, 0.0, 0.0, 0.7), mayChange = True ) OnscreenText(text = 'Press +, - to increase/decrease playing rate', pos = (base.a2dLeft + 0.1, 0.0), align = TextNode.ALeft, scale = 0.05, fg = (1.0, 1.0, 1.0, 1.0), shadow = (0.0, 0.0, 0.0, 0.7), mayChange = True ) OnscreenText(text = 'Press [, ] to decrease/increase sound volume', pos = (base.a2dLeft + 0.1, -0.1), align = TextNode.ALeft, scale = 0.05, fg = (1.0, 1.0, 1.0, 1.0), shadow = (0.0, 0.0, 0.0, 0.7), mayChange = True ) OnscreenText(text = 'Press D, E to disable/enable sounds', pos = (base.a2dLeft + 0.1, -0.2), align = TextNode.ALeft, scale = 0.05, fg = (1.0, 1.0, 1.0, 1.0), shadow = (0.0, 0.0, 0.0, 0.7), mayChange = True ) OnscreenText(text = 'Press B, Shift-B to increase/decrease balance', pos = (base.a2dLeft + 0.1, -0.3), align = TextNode.ALeft, scale = 0.05, fg = (1.0, 1.0, 1.0, 1.0), shadow = (0.0, 0.0, 0.0, 0.7), mayChange = True ) # add our input mappings to the active mappings, the existing mappings were not deleted # but they can be partially overridden by this. self.game.getInput().addMappings('sounds') def exit(self): ''' Performs some cleanup. ''' # call the super class implementation first, this is optional BaseNodeScript.exit(self) if self.instructions is not None: self.instructions.destroy() # this will stop all active sounds if self.sounds is not None: self.sounds.stopAll() def update(self, millis): ''' Gets called on every logic update and we use it to update the UI texts based on user input actions. The millis parameter is the milliseconds that elapsed since the last logic update. ''' # call the super class implementation first, this is optional BaseNodeScript.update(self, millis) # detects if sounds status has been toggled in order to update the UI label if self.lastStatus != self.sounds.isEnabled(): self.lastStatus = self.sounds.isEnabled() self.soundsStatusText.setText('Sounds status: %s' % self.lastStatus) # updates the volume label if needed if self.updateVolume: self.volumeText.setText('Volume: %.1f' % self.sounds.getVolume()) self.updateVolume = False # updates the rate label if needed if self.updateRate: self.rateText.setText('Rate: %.1f' % self.sounds.getPlayRate()) self.updateRate = False # updates the balance label if needed if self.updateBalance: self.balanceText.setText('Balance: %.1f' % self.sounds.getBalance()) self.updateBalance = False # updates the looping label if needed if self.updateLooping: self.loopText.setText('Looping: %d' % self.looping) self.updateLooping = False def onInputAction(self, action): ''' When an input action occurs the active nodescript will get notified through this method. The given parameter is the name of the action that occured. Here we just branch based on the action name. You can see the defined actions of this example in data/example_sounds/sounds.mappings. You return a True value to indicate that you handled the action and False to indicate that you ignored it. ''' if action == "play_blah": ''' We play the blah sound but keep a reference to it in order to have only one instance of this sound. The first time we call sounds.playSound to create the sound and get back a SoundPlaybackInterface which we use for controlling playback. Notice that in the call to playSound we don't provide a filename. That's because the sound's properties, along with the filename, is defined in a .sound file name blah.sound. The file is located at data/example_sounds/blah.sound On subsequent invocations of this action we only call SoundPlaybackInterface.play() to get the sound playing. ''' if self.blahSound is None: self.blahSound = self.sounds.playSound('blah', rate = self.sounds.getPlayRate(), loop = self.looping) else: self.blahSound.play() elif action == "play_cling": ''' The sound is defined in data/example_sounds/cling.sound. We do not keep a reference to the playback interface here. Instead we create a new sound each time the action gets invoked. ''' self.sounds.playSound('cling', rate = self.sounds.getPlayRate(), loop = self.looping) elif action == "play_zipper": ''' For the zipper sound we instead provide a filename and use Sounds.playSoundFile for that purpose. We also don't keep a reference to the playback interface so a new sound is created each time. ''' self.sounds.playSoundFile('zipper_bag_1.ogg', rate = self.sounds.getPlayRate(), loop = self.looping) elif action == "pause_sounds": if self.blahSound is not None: self.blahSound.pause() # Sounds.pauseAll just pauses all currently playing sounds self.sounds.pauseAll() elif action == "resume_sounds": if self.blahSound is not None: self.blahSound.play() # Sounds.resumeAll just pauses all paused sounds self.sounds.resumeAll() elif action == "enable_sounds": # Sounds.enableSounds enables global sound playback self.sounds.enableSounds() elif action == "disable_sounds": # Sounds.enableSounds disables global sound playback, a sound that was playing prior to this call will get stopped self.sounds.disableSounds() elif action == "increase_volume": # this demonstrates updating the properties of a single sound if self.blahSound is not None: self.blahSound.setVolume(self.sounds.getVolume() + 0.1) # while here we update the volume property affecting all sounds at the same time self.sounds.setVolume(self.sounds.getVolume() + 0.1) self.updateVolume = True elif action == "decrease_volume": # same here as we did with the increase_volume action if self.blahSound is not None: self.blahSound.setVolume(self.sounds.getVolume() - 0.1) self.sounds.setVolume(self.sounds.getVolume() - 0.1) self.updateVolume = True elif action == "increase_rate": # same here as we did with the increase_volume action if self.blahSound is not None: self.blahSound.setPlayRate(self.sounds.getPlayRate() + 0.1) self.sounds.setPlayRate(self.sounds.getPlayRate() + 0.1) self.updateRate = True elif action == "decrease_rate": # same here as we did with the increase_volume action if self.blahSound is not None: self.blahSound.setPlayRate(self.sounds.getPlayRate() - 0.1) self.sounds.setPlayRate(self.sounds.getPlayRate() - 0.1) self.updateRate = True elif action == "increase_balance": # same here as we did with the increase_volume action if self.blahSound is not None: self.blahSound.setBalance(self.sounds.getBalance() + 0.1) self.sounds.setBalance(self.sounds.getBalance() + 0.1) self.updateBalance = True elif action == "decrease_balance": # same here as we did with the increase_volume action if self.blahSound is not None: self.blahSound.setBalance(self.sounds.getBalance() - 0.1) self.sounds.setBalance(self.sounds.getBalance() - 0.1) self.updateBalance = True elif action == "increase_loop": # looping in this example only affects the blah sound self.looping += 1 self.updateLooping = True if self.blahSound is not None: self.blahSound.setLoop(self.looping) elif action == "decrease_loop": self.updateLooping = True self.looping -= 1 if self.looping < 0: self.looping = 0 if self.blahSound is not None: self.blahSound.setLoop(self.looping) else: # we didn't handle this, indicate it by returning False return False # action was handled, return True return True def onMessage(self, msg, *args): ''' Using Messenger objects any game component can broadcast messages which will become available to the active nodescript through this method. The msg parameter is the name of the message, while *args is a list of arguments provided by the sender. Each nodescript must first declare the list of messages for which it is interested to receive. The list is declared in the NodeScript.registerMessages method. ''' # When a sound has finished playing the sound_finished message is broadcasted. # We handle it here to get notified when the blah sound has finished and update the UI accordingly. if msg == 'sound_finished': print 'FINISHED' self.eventText.setFg((1.0, 0.0, 0.0, 0.0)) self.eventText.show() Sequence( LerpColorScaleInterval(self.eventText, 0.5, (1,1,1,1), (1,1,1,0)), LerpColorScaleInterval(self.eventText, 0.5, (1,1,1,0), (1,1,1,1)) ).start()
class MouseTunnel(ShowBase): def __init__(self): # Initialize the ShowBase class from which we inherit, which will # create a window and set up everything we need for rendering into it. ShowBase.__init__(self) self.stimtype = 'random image' # session_start self.session_start_time = datetime.datetime.now() # self.accept("escape", sys.exit, [0])#don't let the user do this, because then the data isn't saved. self.accept('q', self.close) self.accept('Q', self.close) self.upArrowIsPressed = base.mouseWatcherNode.isButtonDown(KeyboardButton.up()) self.downArrowIsPressed = base.mouseWatcherNode.isButtonDown(KeyboardButton.down()) self.rightArrowIsPressed = base.mouseWatcherNode.isButtonDown(KeyboardButton.right()) self.leftArrowIsPressed = base.mouseWatcherNode.isButtonDown(KeyboardButton.left()) self.AUTO_REWARD = AUTO_REWARD # disable mouse control so that we can place the camera base.disableMouse() camera.setPosHpr(0, 0, 10, 0, -90, 0) mat = Mat4(camera.getMat()) mat.invertInPlace() base.mouseInterfaceNode.setMat(mat) # base.enableMouse() props = WindowProperties() # props.setOrigin(0, 0) props.setFullscreen(True) props.setCursorHidden(True) props.setMouseMode(WindowProperties.M_relative) base.win.requestProperties(props) base.setBackgroundColor(0, 0, 0) # set the background color to black print('FULSCREEN:') print(props.getFullscreen()) print('=============') # set up the textures # we now get buffer thats going to hold the texture of our new scene altBuffer = self.win.makeTextureBuffer("hello", 1524, 1024) # altBuffer.getDisplayRegion(0).setDimensions(0.5,0.9,0.5,0.8) # altBuffer = base.win.makeDisplayRegion() # altBuffer.makeDisplayRegion(0,1,0,1) # now we have to setup a new scene graph to make this scene self.dr2 = base.win.makeDisplayRegion(0, 0.001, 0, 0.001)#make this really,really small so it's not seeable by the subject altRender = NodePath("new render") # this takes care of setting up ther camera properly self.altCam = self.makeCamera(altBuffer) self.dr2.setCamera(self.altCam) self.altCam.reparentTo(altRender) self.altCam.setPos(0, -10, 0) self.bufferViewer.setPosition("lrcorner") # self.bufferViewer.position = (-.1,-.4,-.1,-.4) self.bufferViewer.setCardSize(1.0, 0.0) print(self.bufferViewer.position) self.imagesTexture = MovieTexture("image_sequence") # success = self.imagesTexture.read("models/natural_images.avi") # success = self.imagesTexture.read("models/movie_5hz.mpg") self.imagesTexture.setPlayRate(1.0) self.imagesTexture.setLoopCount(10) # self.imageTexture =loader.loadTexture("models/NaturalImages/BSDs_8143.tiff") # self.imagesTexture.reparentTo(altRender) self.fixationPoint = OnscreenImage(image='models/fixationpoint.jpg', pos=(0, 0,0),scale=0.01) cm = CardMaker("stimwindow") cm.setFrame(-4, 4, -3, 3) # cm.setUvRange(self.imagesTexture) self.card = NodePath(cm.generate()) self.card.reparentTo(altRender) if self.stimtype == 'image_sequence': self.card.setTexture(self.imagesTexture, 1) # add the score display self.scoreLabel = OnscreenText(text='Current Score:', pos=(-1, 0.9), scale=0.1, fg=(0.8, 0.8, 0.8, 1)) self.scoreText = OnscreenText(text=str(0), pos=(-1, 0.76), scale=0.18, fg=(0, 1, 0, 1), shadow=(0.1, 1, 0.1, 0.5)) self.feebackScoreText = OnscreenText(text='+ ' + str(0), pos=(-0.5, 0.5), scale=0.3, fg=(0, 1, 0, 1), shadow=(0.1, 1, 0.1, 0.5)) self.feebackScoreText.setX(3.) # self.imagesTexture.play() # self.bufferViewer.setPosition("lrcorner") # self.bufferViewer.setCardSize(1.0, 0.0) self.accept("v", self.bufferViewer.toggleEnable) self.accept("V", self.bufferViewer.toggleEnable) # Load the tunnel self.initTunnel() # initialize some things # for the tunnel construction: self.boundary_to_add_next_segment = -1 * TUNNEL_SEGMENT_LENGTH self.current_number_of_segments = 8 # task flow booleans self.in_waiting_period = False self.stim_started = False self.looking_for_a_cue_zone = True self.in_reward_window = False self.show_stimulus = False # for task control self.interval = 0 self.time_waiting_in_cue_zone = 0 self.wait_time = 1.83 self.stim_duration = 0 # in seconds # self.distribution_type = np.random.uniform# # self.distribution_type_inputs = [0.016,0.4] #change the min & max stim duration times #New lines EAS: set weights higher for faster image durations self.durWeights = list() a = np.linspace(0.016,0.4,10) for i,j in enumerate(a): if j<0.1: p1 = 0.25 self.durWeights.append(p1) elif j > 0.1 and j < 0.21: p1 = 0.1 self.durWeights.append(p1) elif j> 0.21: p1 = 0.04 self.durWeights.append(p1) self.rng = np.random.default_rng() a = np.asarray(a) self.distribution_type_inputs = a #subset_size = len(p) #End new lines # self.distribution_type_inputs = [0.05,1.5] #can be anytong should match # self.distribution_type_inputs = [0.016,0.4] #change the min & max stim duration times # self.distribution_type_inputs = [0.016,0.4, 10] #change the min & max stim duration times self.max_stim_duration = 1.0 # in seconds self.stim_elapsed = 0.0 # in seconds self.last_position = base.camera.getZ() self.position_on_track = base.camera.getZ() # for reward control self.reward_window = REWARD_WINDOW # in seconds self.reward_elapsed = 0.0 # self.new_dt = list() # self.reward_volume = 0.008 # in mL. this is for the hardcoded 0.1 seconds of reward time self.reward_volume = int(REWARD_VOLUME) # in uL, for the stepper motor self.reward_time = 0.1 # in sec, based on volume. hard coded right now but should be modified by the (1) calibration and (2) optionally by the main loop for dynamic reward scheduling # self.lick_buffer = [] self.current_score = 0 self.score = 0 self.feedback_score_startime = -2 # INITIALIZE NIDAQ self.nidevice = 'Dev2' self.encodervinchannel = 1 self.encodervsigchannel = 0 self.invertdo = False self.diport = 1 self.lickline = 1 self.doport = 0 self.rewardline = 0 self.rewardlines = [0] self.encoder_position_diff = 0 if have_nidaq: self._setupDAQ() self.do.WriteBit(1, 1) self.do.WriteBit(3, 1) # set reward high, because the logic is flipped somehow. possibly by haphazard wiring of the circuit (12/24/2018 djd) self.previous_encoder_position = self.ai.data[0][self.encodervsigchannel] else: self.previous_encoder_position = 0 self.encoder_gain = 3 # INITIALIZE LICK SENSOR self._lickSensorSetup() # INITIALIZE output data self.lickData = [] self.x = [] self.t = [] self.trialData = [] self.reactionTimeData = [] self.rewardData = [] self.rightKeyData = [] self.leftKeyData = [] self.imageData = [] self.imageTimeData = [] self.scoreData = [] self.trialDurationData = [] self.new_dt = [] # INITIALIZE KEY SENSOR, for backup inputs and other user controls self.keys = key.KeyStateHandler() self.accept('r', self._give_reward, [self.reward_volume]) self.accept('l', self._toggle_reward) # initialize the image list and populate what images you want included # self.img_list = glob.glob('models/2AFC_IMAGES_HUMAN/*.tif') # self.img_list = glob.glob('models/2AFC_IMAGES_HUMAN2/*.tif') # self.img_list = glob.glob('/Users/elizabethstubblefield/Desktop/cheetah_or_elephant/composite_images/masks/all_same_num_ea/*.tif') #Newest images self.img_list = glob.glob('models/all_same_ea/*.tif') #Newest images #No longer hard-coded: self.original_indices = [0,0] #this was manually counted... first number must was the index of the first easy img; was [43, -18] for ndx, name in enumerate(self.img_list): if 'Cheetah255' in name: self.original_indices[0] = ndx elif 'Elephant0' in name: self.original_indices[1] = ndx # print(self.img_list) # self.original_indices = [43,-18] #manually counted, grump #Problematic w/out at least 43 images in the folder self.imageTextures =[loader.loadTexture(img) for img in self.img_list] self.img_id = None #this variable is used so we know which stimulus is being presented self.img_mask = None #this tells us what the image mask being presented is # self._setupEyetracking() # self._startEyetracking() if AUTO_MODE: self.gameTask = taskMgr.add(self.autoLoop2, "autoLoop2") self.rewardTask = taskMgr.add(self.rewardControl, "reward") self.cue_zone = concatenate((self.cue_zone, arange( \ self.current_number_of_segments * -TUNNEL_SEGMENT_LENGTH-50, \ self.current_number_of_segments * -TUNNEL_SEGMENT_LENGTH - TUNNEL_SEGMENT_LENGTH - 100, \ -1))) self.auto_position_on_track = 0 self.auto_restart = False self.auto_running = True self.contTunnel() else: # Now we create the task. taskMgr is the task manager that actually # calls the function each frame. The add method creates a new task. # The first argument is the function to be called, and the second # argument is the name for the task. It returns a task object which # is passed to the function each frame. self.gameTask = taskMgr.add(self.gameLoop, "gameLoop") # self.stimulusTask = taskMgr.add(self.stimulusControl, "stimulus") self.lickTask = taskMgr.add(self.lickControl, "lick") self.rewardTask = taskMgr.add(self.rewardControl, "reward") self.keyTask = taskMgr.add(self.keyControl, "Key press") # Code to initialize the tunnel def initTunnel(self): self.tunnel = [None] * 8 for x in range(8): # Load a copy of the tunnel self.tunnel[x] = loader.loadModel('models/tunnel') # The front segment needs to be attached to render if x == 0: self.tunnel[x].reparentTo(render) # The rest of the segments parent to the previous one, so that by moving # the front segement, the entire tunnel is moved else: self.tunnel[x].reparentTo(self.tunnel[x - 1]) # We have to offset each segment by its length so that they stack onto # each other. Otherwise, they would all occupy the same space. self.tunnel[x].setPos(0, 0, -TUNNEL_SEGMENT_LENGTH) # Now we have a tunnel consisting of 4 repeating segments with a # hierarchy like this: # render<-tunnel[0]<-tunnel[1]<-tunnel[2]<-tunnel[3] self.tunnel[0] = loader.loadModel('models/grating') self.tunnel[0].reparentTo(render) self.cue_zone = arange(0, TUNNEL_SEGMENT_LENGTH, -1)-280 # This function is called to snap the front of the tunnel to the back # to simulate traveling through it def contTunnel(self): self.auto_position_on_track -= 50 position_on_track = self.auto_position_on_track print(str(int(position_on_track)) + ' ' + str(self.cue_zone)) if int(position_on_track) in np.array(self.cue_zone): # check for cue zone if not self.auto_restart: print('STOP!') self.tunnelMove.pause() self.auto_presentation = True # self.current_number_of_segments +=1 else: self.auto_restart = True self.tunnelMove.resume() else: self.in_waiting_period = False self.auto_presentation = False # base.setBackgroundColor([1,0 , 0]) if self.looking_for_a_cue_zone == False: self.looking_for_a_cue_zone = True if self.stim_started == True: self.stop_a_presentation() # This line uses slices to take the front of the list and put it on the # back. For more information on slices check the Python manual self.tunnel = self.tunnel[1:] + self.tunnel[0:1] # Set the front segment (which was at TUNNEL_SEGMENT_LENGTH) to 0, which # is where the previous segment started self.tunnel[0].setZ(0) # Reparent the front to render to preserve the hierarchy outlined above self.tunnel[0].reparentTo(render) # Set the scale to be apropriate (since attributes like scale are # inherited, the rest of the segments have a scale of 1) self.tunnel[0].setScale(.155, .155, .305) # Set the new back to the values that the rest of the segments have self.tunnel[3].reparentTo(self.tunnel[2]) self.tunnel[3].setZ(-TUNNEL_SEGMENT_LENGTH) self.tunnel[3].setScale(1) # Set up the tunnel to move one segment and then call contTunnel again # to make the tunnel move infinitely self.tunnelMove = Sequence( LerpFunc(self.tunnel[0].setZ, duration=TUNNEL_TIME, fromData=0, toData=TUNNEL_SEGMENT_LENGTH * .305), Func(self.contTunnel) ) self.tunnelMove.start() def get_trial_duration(self): #EAS updated 10.5.20 self.stim_duration = self.rng.choice(self.distribution_type_inputs, 1, p=self.durWeights) #pull out one value in array a, w/ probability based on weights return self.stim_duration[0] # return self.distribution_type(*self.distribution_type_inputs) def start_a_presentation(self): self.save_data() self.stim_duration = self.get_trial_duration() self.fixationPoint.destroy() # import pickle # with open('objs.pkl', 'wb') as f: # Python 3: open(..., 'wb') # pickle.dump([self.stim_duration,self.distribution_type_inputs], f) # print('variables saved!!!') self.in_reward_window = True print("start") if have_nidaq: self.do.WriteBit(2, 1) # self.bufferViewer.toggleEnable() self.lick_buffer = [] self.trialData.extend([globalClock.getFrameTime()]) self.reactionTimeData.extend([-1]) if self.stimtype == 'random image': if len(self.trialData) < 4: i=self.original_indices[int(np.round(np.random.random()))] #commented out due to <43 images [see l. 290ish] self.stim_duration = 2.0 else: #commented out due to <43 images i = randint(len(self.imageTextures)) self.card.setTexture(self.imageTextures[i],0) self.dr2.setDimensions(0.25, 0.75, 0.25, 0.75) # floats (left, right, bottom, top) self.img_id = self.img_list[i] #this assigns the current presented image to self.image_id print(self.img_id) self.imageData.extend(self.img_id) if self.stimtype == 'image_sequence': self.imagesTexture.setTime(0.) self.dr2.setDimensions(0.4, 0.8, 0.4, 0.70) # floats (left, right, bottom, top) self.imagesTexture.play() self.imageTimeData.extend([globalClock.getFrameTime()]) self.imageData.extend(self.img_id) def check_arrows(self): # this function will report which arrow was pressed. right = 1, left = 2, no press = 0 if self.rightArrowIsPressed: #print('check arrows RIGHT') return 1 # time.sleep(2) elif self.leftArrowIsPressed: #print('check arrows LEFT') return 2 # time.sleep(2) else: return 0 def stop_a_presentation(self): if self.stim_started == True: if self.stim_elapsed > self.stim_duration: self.trialDurationData.append(self.stim_duration) else: self.trialDurationData.append(self.stim_duration) self.dr2.setDimensions(0, 0.001, 0, 0.001)#make this really,really small so it's not seeable by the subject # self.bufferViewer.toggleEnable() self.stim_started = False self.stim_elapsed = 0. self.stim_duration = 0. self.stim_off_time = globalClock.getFrameTime() if have_nidaq: self.do.WriteBit(2, 0) # if globalClock.getFrameTime() > self.feedback_score_startime + 1.5: # arbitrary wait to make sure this isn't after a correct trial # self.feebackScoreText.setText('+' + str(0)) # self.feebackScoreText.setFg((1, 0, 0, 1)) # self.feebackScoreText.setX(.5) # self.feedback_score_startime = globalClock.getFrameTime() self.scoreData.append(self.current_score) self.fixationPoint = OnscreenImage(image='models/fixationpoint.jpg', pos=(0, 0,0),scale=0.01) # def keySensorSetup(self): def show_the_score(self): self.feebackScoreText.setText('+' + str(self.score)) if self.score == 0: self.feebackScoreText.setFg((1, 0, 0, 1)) else: self.feebackScoreText.setFg((0, 1, 0, 1)) self.feebackScoreText.setX(.5) self.feedback_score_startime = globalClock.getFrameTime() # sets up the task to listen for user input from the keys def _lickSensorSetup(self): """ Attempts to set up lick sensor NI task. """ ##TODO: Make lick sensor object if necessary. Let user select port and line. if have_nidaq: if self.di: self.lickSensor = self.di # just use DI for now licktest = [] for i in range(30): licktest.append(self.rightArrowIsPressed.Read()[self.lickline]) time.sleep(0.01) licktest = np.array(licktest, dtype=np.uint8) if len(licktest[np.where(licktest > 0)]) > 25: self.lickSensor = None self.lickData = [np.zeros(len(self.rewardlines))] print("Lick sensor failed startup test.") else: print('lick sensor setup succeeded.') self.keycontrol = True else: print("Could not initialize lick sensor. Ensure that NIDAQ is connected properly.") self.keycontrol = True self.lickSensor = None self.lickData = [np.zeros(len(self.rewardlines))] self.keys = key.KeyStateHandler() # self.window.winHandle.push_handlers(self.keys) else: print("Could not initialize lick sensor. Ensure that NIDAQ is connected properly.") self.keycontrol = True self.lickSensor = None self.lickData = [np.zeros(len(self.rewardlines))] self.keys = key.KeyStateHandler() def reactiontime_to_score(self): rt = globalClock.getFrameTime() - self.trialData[-1] self.reactionTimeData[-1] = rt score = 100. / rt # scale the reaction time, with faster producing more points return int(score / 20.) # def _read_licks(self): # not yet implemented; should be replaces with check to beam break def _give_reward(self, volume): print("reward!") self.rewardData.extend([globalClock.getFrameTime()]) # for humans self.score = self.reactiontime_to_score() self.current_score += self.score self.show_the_score() self.scoreText.setText(str(self.current_score)) # for mice if have_nidaq: self.do.WriteBit(3, 0) time.sleep(self.reward_time) self.do.WriteBit(3, 1) # put a TTL on a line to indicate that a reward was given s.dispense(volume) # pass # not yet implemented def _toggle_reward(self): if self.AUTO_REWARD: self.AUTO_REWARD = False print('switched to lick sensing for reward.') else: self.AUTO_REWARD = True print('switched to automatic rewards after stimuli.') def autoLoop2(self, task): dt = globalClock.getDt() current_time = globalClock.getFrameTime() self.x.extend([self.auto_position_on_track]) self.t.extend([globalClock.getFrameTime()]) if self.auto_presentation: self.auto_running = False if self.in_waiting_period: self.time_waited += dt else: self.time_waited = 0 self.in_waiting_period = True if self.time_waited > self.wait_time: # if in cue zone,see if we have been ther for long enough # start a trial self.start_position = self.auto_position_on_track self.start_time = current_time if not self.stim_started: self.start_a_presentation() # print(self.stim_duration) self.stim_started = True self.show_stimulus = True else: self.stim_elapsed += dt if self.stim_elapsed > self.stim_duration: self.show_stimulus = False self.in_reward_window = True self.stop_a_presentation() self.auto_restart = False # print(self.current_number_of_segments) self.current_number_of_segments += 9 # redefine the cue zone as the next one self.cue_zone = arange(self.current_number_of_segments * -TUNNEL_SEGMENT_LENGTH, self.current_number_of_segments * -TUNNEL_SEGMENT_LENGTH - TUNNEL_SEGMENT_LENGTH - 280, -1) # extend cue zone, keeping old ones # self.cue_zone = concatenate((self.cue_zone,arange(self.current_number_of_segments*-TUNNEL_SEGMENT_LENGTH-40, # self.current_number_of_segments*-TUNNEL_SEGMENT_LENGTH-TUNNEL_SEGMENT_LENGTH-40, # -1))) self.contTunnel() self.time_waited = 0 self.looking_for_a_cue_zone = False # base.setBackgroundColor([0, 0, 1]) else: pass # base.setBackgroundColor([0, 1, 0]) else: self.auto_running = True return Task.cont # Since every return is Task.cont, the task will def gameLoop(self, task): # get the time elapsed since the next frame. dt = globalClock.getDt() self.new_dt.append(dt) #Store the append here for dt current_time = globalClock.getFrameTime() #This is for the key press if current_time > self.feedback_score_startime + 1.5: self.feebackScoreText.setX(3.) # get the camera position. position_on_track = base.camera.getZ() # get the encoder position from NIDAQ Analog Inputs channel 2 if have_nidaq: encoder_position = self.ai.data[0][self.encodervsigchannel] # zeroth sample in buffer [0], from ai2 [2] # convert to track coordinates encoder_position_diff = (encoder_position - self.previous_encoder_position) if encoder_position_diff > 4.5: encoder_position_diff -= 5. if encoder_position_diff < -4.5: encoder_position_diff += 5. self.encoder_position_diff = encoder_position_diff * self.encoder_gain self.previous_encoder_position = encoder_position else: self.read_keys() if self.upArrowIsPressed: self.encoder_position_diff = -1 * self.encoder_gain if self.downArrowIsPressed: self.encoder_position_diff = 1 * self.encoder_gain if not self.downArrowIsPressed: if not self.upArrowIsPressed: self.encoder_position_diff = 0 position_on_track = base.camera.getZ() + self.encoder_position_diff # reset the camera position self.camera.setPos(base.camera.getX(), base.camera.getY(), position_on_track) self.x.extend([position_on_track]) self.t.extend([globalClock.getFrameTime()]) # first check if the mouse moved on the last frame. if abs(self.last_position - position_on_track) < 1.5: # the mouse didn't move more than 0.5 units on the track self.moved = False if int(position_on_track) in self.cue_zone: # check for cue zone if self.looking_for_a_cue_zone: # make sure we transitioning from the tunnel to a cue zone # increment how long we've been waiting in the cue zone. if self.in_waiting_period: self.time_waited += dt else: self.time_waited = 0 self.in_waiting_period = True if self.time_waited > self.wait_time: # if in cue zone,see if we have been ther for long enough # start a trial self.start_position = position_on_track self.start_time = current_time if not self.stim_started: self.start_a_presentation() print(self.stim_duration) self.stim_started = True self.show_stimulus = True else: self.stim_elapsed += dt if self.stim_elapsed > self.stim_duration: self.show_stimulus = False self.stop_a_presentation() self.time_waited = 0 self.looking_for_a_cue_zone = False # base.setBackgroundColor([0, 0, 1]) else: pass # base.setBackgroundColor([0, 1, 0]) else: self.in_waiting_period = False # base.setBackgroundColor([1,0 , 0]) if self.looking_for_a_cue_zone == False: self.looking_for_a_cue_zone = True if self.stim_started == True: self.stop_a_presentation() else: # the mouse did move self.moved = True if self.stim_started == True: # check if it moved during a presenation self.stop_a_presentation() self.time_waited = 0 self.looking_for_a_cue_zone = False self.show_stimulus = False # if we need to add another segment, do so if position_on_track < self.boundary_to_add_next_segment: self.tunnel.extend([None]) x = self.current_number_of_segments if x % 8 == 0: self.tunnel[x] = loader.loadModel('models/grating') self.cue_zone = concatenate((self.cue_zone, arange( \ self.current_number_of_segments * -TUNNEL_SEGMENT_LENGTH - 50, \ self.current_number_of_segments * -TUNNEL_SEGMENT_LENGTH - TUNNEL_SEGMENT_LENGTH - 100, \ -1))) else: self.tunnel[x] = loader.loadModel('models/tunnel') self.tunnel[x].setPos(0, 0, -TUNNEL_SEGMENT_LENGTH) self.tunnel[x].reparentTo(self.tunnel[x - 1]) # increment self.boundary_to_add_next_segment -= TUNNEL_SEGMENT_LENGTH self.current_number_of_segments += 1 else: pass # print('current:'+str(position_on_track) +' next boundary:' + str(self.boundary_to_add_next_segment)) self.last_position = position_on_track # lick_times = self. # self._read_licks() return Task.cont # Since every return is Task.cont, the task will # continue indefinitely, under control of the mouse (animal) def stimulusControl(self, task): if self.show_stimulus and not self.bufferViewer.isEnabled(): # self.bufferViewer.toggleEnable() self.dr2.setDimensions(0.5, 0.9, 0.5, 0.8) if not self.show_stimulus and self.bufferViewer.isEnabled(): # self.bufferViewer.toggleEnable() self.dr2.setDimensions(0, 0.1, 0, 0.1) return Task.cont def lickControl(self, task): """ Checks to see if a lick is occurring. """ ##TODO: Let user select line for lick sensing. if self.lickSensor: if self.lickSensor.Read()[self.lickline]: self.lickData.extend([globalClock.getFrameTime()]) print('lick happened at: ' + str(self.lickData[-1])) elif self.keycontrol == True: # NO NI BOARD. KEY INPUT? if self.keys[key.SPACE]: data = [globalClock.getFrameTime()] elif self.keys[key.NUM_1]: # print(self.lickData) # elif self.keys[key.NUM_3]: # data = [0,1] # else: # data = [0,0] self.lickData.extend(data) return Task.cont def keyControl(self, task): # listen to and record when the arrows are pressed self.read_keys() if self.rightArrowIsPressed: self.rightKeyData.extend([globalClock.getFrameTime()]) print('right arrow at: ' + str(self.rightKeyData[-1])) # time.sleep(.2) #EAS changed this only allows for one keystroke to be recorded every 0.5s elif self.leftArrowIsPressed: self.leftKeyData.extend([globalClock.getFrameTime()]) print('left arrow at: ' + str(self.leftKeyData[-1])) # time.sleep(.2) #EAS changed this only allows for one keystroke to be recorded every 0.5s return Task.cont def rewardControl(self, task): # print(self.in_reward_window) if self.in_reward_window == True: if self.reward_elapsed < self.reward_window: self.reward_elapsed += globalClock.getDt() # self.new_reward_elapsed.append(self.reward_elapsed) # import pickle # with open('objs.pkl', 'wb') as f: # Python 3: open(..., 'wb') # pickle.dump([self.stim_duration,self.distribution_type_inputs, self.new_rew_control], f) # print('variables saved!!!') self.check_arrows() if not self.AUTO_REWARD: if self.check_arrows() == 1: # if check arrows returns a 1 the right arrow was pressed during stimulus presentation # note reaction time if self.img_id.find("Elephant") != -1: #if the current image file contains the string in () the value is not -1 self._give_reward(self.reward_volume) print('correct: image was mostly elephant') self.in_reward_window = False; self.reward_elapsed = 0. # reset elif 'Same' in self.img_id: #updated 8.24.20: reward 50/50 images 1/2 the time if round(np.random.random(1)[0]): self._give_reward(self.reward_volume) print('correct: image was same') self.in_reward_window = False; self.reward_elapsed = 0. # reset else: print(self.img_mask) print('incorrect: image was same') self.in_reward_window = False self.reward_elapsed = 0. # reset self.score=0 self.show_the_score() else: print(self.img_mask) print('image was not mostly elephant!') self.in_reward_window = False self.reward_elapsed = 0. # reset self.score=0 self.show_the_score() if self.check_arrows() == 2: #if check arrows returns a 2 the left arrow was pressed during stimulus presentation if self.img_id.find("Cheetah") != -1: #if the current image file contains the string in () the value is not -1 self._give_reward(self.reward_volume) print('correct: image was mostly cheetah') self.in_reward_window = False; self.reward_elapsed = 0. # reset elif 'Same' in self.img_id: #updated 8.24.20: reward 50/50 images 1/2 the time if round(np.random.random(1)[0]): self._give_reward(self.reward_volume) print('correct: image was same') self.in_reward_window = False; self.reward_elapsed = 0. # reset else: print(self.img_mask) print('incorrect: image was same') self.in_reward_window = False self.reward_elapsed = 0. # reset self.score=0 self.show_the_score() else: print(self.img_mask) print('image was not mostly cheetah!') self.in_reward_window = False self.reward_elapsed = 0. # reset self.score=0 self.show_the_score() else: self._give_reward(self.reward_volume) self.in_reward_window = False; self.reward_elapsed = 0. # reset # base.setBackgroundColor([1, 1, 0]) # # if self.keys[key.NUM_1]: # # print('reward!') else: self.score=0 self.show_the_score() self.in_reward_window = False self.reward_elapsed = 0. else: pass#print('not listening for reward'+str(globalClock.getFrameTime())) return Task.cont def _setupEyetracking(self): """ sets up eye tracking""" try: eyetrackerip = "DESKTOP-EE5KKDO" eyetrackerport = 10000 trackeyepos = False from aibs.Eyetracking.EyetrackerClient import Client self.eyetracker = Client(outgoing_ip=eyetrackerip, outgoing_port=eyetrackerport, output_filename=str(datetime.datetime.now()).replace(':', '').replace('.', '').replace( ' ', '-')) self.eyetracker.setup() # eyedatalog = [] # if trackeyepos: # eyeinitpos = None except: print("Could not initialize eyetracker:") self.eyetracker = None def _startEyetracking(self): if self.eyetracker: self.eyetracker.recordStart() def _setupDAQ(self): """ Sets up some digital IO for sync and tiggering. """ print('SETTING UP DAQ') try: if self.invertdo: istate = 'low' else: istate = 'high' self.do = DigitalOutput(self.nidevice, self.doport, initial_state='low') self.do.StartTask() except: # Exception, e: print("Error starting DigitalOutput task:") self.do = None try: self.di = DigitalInput(self.nidevice, self.diport) self.di.StartTask() except: # Exception, e: print("Error starting DigitalInput task:") self.di = None # try: # set up 8 channels, only use 2 though for now self.ai = AnalogInput(self.nidevice, range(8), buffer_size=25, terminal_config='RSE', clock_speed=6000.0, voltage_range=[-5.0, 5.0]) self.ai.StartTask() # except:# Exception, e: # print("Error starting AnalogInput task:") # self.ai = None try: self.ao = AnalogOutput(self.nidevice, channels=[0, 1], terminal_config='RSE', voltage_range=[0.0, 5.0]) self.ao.StartTask() except: # Exception, e: print("Error starting AnalogOutput task:") self.ao = None def read_keys(self): self.upArrowIsPressed = base.mouseWatcherNode.isButtonDown(KeyboardButton.up()) self.downArrowIsPressed = base.mouseWatcherNode.isButtonDown(KeyboardButton.down()) self.rightArrowIsPressed = base.mouseWatcherNode.isButtonDown(KeyboardButton.right()) self.leftArrowIsPressed = base.mouseWatcherNode.isButtonDown(KeyboardButton.left()) def save_data(self): if not os.path.isdir(os.path.join(os.getcwd(), 'data')): os.mkdir(os.path.join(os.getcwd(), 'data')) save_path = os.path.join(os.getcwd(), 'data', str(MOUSE_ID) + '_' + \ str(self.session_start_time.year) + '_' + \ str(self.session_start_time.month) + '_' + \ str(self.session_start_time.day) + '-' + \ str(self.session_start_time.hour) + '_' + \ str(self.session_start_time.minute) + '_' + \ str(self.session_start_time.second)) if not os.path.isdir(save_path): os.mkdir(save_path) print("saving data to " + save_path) np.save(os.path.join(save_path, 'licks.npy'), self.lickData) np.save(os.path.join(save_path, 'x.npy'), self.x) np.save(os.path.join(save_path, 't.npy'), self.t) np.save(os.path.join(save_path, 'trialData.npy'), self.trialData) np.save(os.path.join(save_path, 'rewardData.npy'), self.rewardData) np.save(os.path.join(save_path, 'rtData.npy'), self.reactionTimeData) np.save(os.path.join(save_path, 'rightKeyData.npy'), self.rightKeyData) np.save(os.path.join(save_path, 'leftKeyData.npy'), self.leftKeyData) np.save(os.path.join(save_path, 'imageData.npy'), self.imageData) np.save(os.path.join(save_path, 'imageTimeData.npy'), self.imageTimeData) np.save(os.path.join(save_path, 'scoreData.npy'), self.scoreData) np.save(os.path.join(save_path, 'trialDurationData.npy'), self.trialDurationData) np.save(os.path.join(save_path, 'dT.npy'), self.new_dt) def close(self): self.save_data() print('rewardData:') print(np.shape(self.rewardData)) try: #push anonymized data to Denman Lab Open Science Framework project for human psychophysics subprocess.call('osf -p 7xruh -u [email protected] upload -r '+save_path+' data/'+os.path.basename(save_path),shell=True) #commented these lines out to test if saving to osf is the hangup; it's not except:pass sys.exit(0)
class MyApp(ShowBase): gameMode = "Intro" def __init__(self): ShowBase.__init__(self) #self.cameraSetup() self.setupKeyboard() self.initIntro() def gameChangeMode(self): if base.gameMode == "Intro": self.initIntro() elif base.gameMode == "Convo": self.initConvo() def initIntro(self): self.fadeAlpha = 0.0 self.fadeTime = 30.0 self.fadeInc = 1.0/self.fadeTime self.fadeDir = "up" self.sceneImage = OnscreenImage(image = "images/scene.png", scale = (1920.0/1080.0,1,1)) self.sceneImage.setTransparency(TransparencyAttrib.MAlpha) self.sceneImage.setAlphaScale(self.fadeAlpha) self.imageNumber = 0 self.sceneImageList = ["images/scene.png","images/image1.jpg"] #self.texty = OnscreenText(str(self.fadeAlpha)) taskMgr.add(self.gameIntroUpdate, "GameIntroUpdate") def gameIntroUpdate(self, task): self.slideManager() if (base.gameMode != "Intro"): self.gameChangeMode() return Task.done return Task.cont def cameraSetup(self): base.camLens.setAspectRatio(1920.0/1080.0) def slideManager(self): if self.fadeDir == "up" and self.fadeAlpha<1: self.fadeAlpha += self.fadeInc self.sceneImage.setAlphaScale(self.fadeAlpha) #self.texty.setText(str(self.fadeAlpha)) if self.fadeDir == "down" and self.fadeAlpha > 0: self.fadeAlpha -= self.fadeInc self.sceneImage.setAlphaScale(self.fadeAlpha) if self.fadeDir == "up" and self.fadeAlpha >= 1: self.fadeDir = "down" if self.fadeDir == "down" and self.fadeAlpha <= 0: if self.imageNumber < 1: self.fadeDir = "up" self.sceneImage.setImage(self.nextImage()) self.sceneImage.setTransparency(TransparencyAttrib.MAlpha) self.sceneImage.setAlphaScale(self.fadeAlpha) else: self.fadeDir = "up" base.gameMode = "Convo" self.sceneImage.setImage(self.sceneImageList[self.imageNumber]) self.sceneImage.setTransparency(TransparencyAttrib.MAlpha) self.sceneImage.setAlphaScale(self.fadeAlpha) def nextImage(self): self.imageNumber += 1 return self.sceneImageList[self.imageNumber] def initConvo(self): self.textToType = "This will be used for talking\nThis is a good conversation box" self.textVisible = "" self.strOptionA = "Yes" self.strOptionB = "No" self.strOptionC = "Maybe" self.convoResponseSelected = False self.convoDepth = "1" self.intOptionCount = 3 #self.txtConvo = OnscreenText(self.textVisible, align = TextNode.ALeft) self.txtConvo = TextNode("Texty Bastard") self.txtConvo.setText(self.textToType) self.txtReply = TextNode("reply") self.txtReply.setText("") self.intHover = 0 #self.txtConvo.setFrameColor(0, 0, 1, 1) #self.txtConvo.setFrameAsMargin(0.2, 0.2, 0.1, 0.1) myFrame = DirectFrame(frameColor=(0.8, 0.8, 0.8, 0.4), frameSize=(-1, 0, -1, 0)) nodePathConvo = aspect2d.attachNewNode(self.txtConvo) nodePathConvo.setScale(0.07) nodePathConvo.setPos(-1, 0, -0.1) self.i = 0 self.indent = 0.0 self.selectedResponse = 0 self.txtOptionA = OnscreenText(text = "", pos = (-1,-0.6), align = TextNode.ALeft) self.txtOptionB = OnscreenText(text = "", pos = (-1,-0.7), align = TextNode.ALeft) self.txtOptionC = OnscreenText(text = "", pos = (-1,-0.8), align = TextNode.ALeft) taskMgr.add(self.gameConvoUpdate, "GameConvoUpdate") def gameConvoUpdate(self, task): if (len(self.textVisible) != len(self.textToType)): task.delayTime = 0.001 while (self.i < len(self.textToType)): self.textVisible += self.textToType[self.i] self.txtConvo.setText(self.textVisible) self.i += 1 return Task.again else: taskMgr.add(self.gameConvoOptions, "ConvoOptions") return Task.done def gameConvoOptions(self, task): self.txtOptionA.setText(self.strOptionA) self.txtOptionB.setText(self.strOptionB) self.txtOptionC.setText(self.strOptionC) if self.convoResponseSelected == True: if self.convoCheckBranch("01", self.convoDepth): #self.txtConvo.setText(self.convoGetStrings("01", self.convoDepth)) self.convoNextDialogue("01", self.convoDepth) else: #self.txtConvo.setText("It DIDn't worked") self.convoDepth = "1" self.convoNextDialogue("01", self.convoDepth) return Task.done elif self.selectedResponse == 0: if self.intOptionCount > 0: self.txtOptionA.setX(-1+self.indent) self.txtOptionA.setFg(fg = (1,0,0,1)) if self.intOptionCount > 1: self.txtOptionB.setX(-1) self.txtOptionB.setFg(fg = (0,0,0,1)) if self.intOptionCount > 2: self.txtOptionC.setX(-1) self.txtOptionC.setFg(fg = (0,0,0,1)) self.indent = self.getIndent(self.indent,0.01,0.1) return Task.again elif self.selectedResponse == 1: if self.intOptionCount > 0: self.txtOptionA.setX(-1) self.txtOptionA.setFg(fg = (0,0,0,1)) if self.intOptionCount > 1: self.txtOptionB.setX(-1+self.indent) self.txtOptionB.setFg(fg = (1,0,0,1)) if self.intOptionCount > 2: self.txtOptionC.setX(-1) self.txtOptionC.setFg(fg = (0,0,0,1)) self.indent = self.getIndent(self.indent,0.01,0.1) return Task.again elif self.selectedResponse == 2: if self.intOptionCount > 0: self.txtOptionA.setX(-1) self.txtOptionA.setFg(fg = (0,0,0,1)) if self.intOptionCount > 1: self.txtOptionB.setX(-1) self.txtOptionB.setFg(fg = (0,0,0,1)) if self.intOptionCount > 2: self.txtOptionC.setX(-1+self.indent) self.txtOptionC.setFg(fg = (1,0,0,1)) self.indent = self.getIndent(self.indent,0.01,0.1) return Task.again def convoGetStrings(self,npcId,depth): if self.convoCheckBranch(npcId,depth) != True: return "#Error# String Not Found\nLooking for: "+"("+str(npcId)+")."+depth+":" char = "" line = "" feed = "" path = os.path.abspath(os.getcwd()) f = open(path+"\\text\\stringsConvo.txt","r") while line != "("+str(npcId)+")."+depth+":": while char != ":": char = f.readline(1) line += char feed += "\n"+line if line != "("+str(npcId)+")."+depth+":": char = "" line = "" f.readline() print(feed + " Selected") f.readline(1) line = f.readline() line = line.replace("##", "\n") f.close() return line def convoCheckBranch(self,npcId,depth): path = os.path.abspath(os.getcwd()) f = open(path+"\\text\\stringsConvo.txt","r") char = "" line = "" branchFound = False while line != "<END>:": char = "" line = "" while char != ":": char = f.readline(1) line += char if line == "<END>:": f.close() return False elif line == "("+str(npcId)+")."+str(depth)+":": f.close() return True else: f.readline() def convoNextDialogue(self,npcId,depth): self.textToType = self.convoGetStrings(npcId, depth) self.intOptionCount = self.convoGetOptionCount(npcId, depth) if self.intOptionCount == 1: self.strOptionA = self.convoGetStrings(npcId, depth+".A") self.strOptionB = "" self.strOptionC = "" elif self.intOptionCount == 2: self.strOptionA = self.convoGetStrings(npcId, depth+".A") self.strOptionB = self.convoGetStrings(npcId, depth+".B") self.strOptionC = "" elif self.intOptionCount == 3: self.strOptionA = self.convoGetStrings(npcId, depth+".A") self.strOptionB = self.convoGetStrings(npcId, depth+".B") self.strOptionC = self.convoGetStrings(npcId, depth+".C") else: self.strOptionA = "" self.strOptionB = "" self.strOptionC = "" self.textVisible = "" self.txtOptionA.setText("") self.txtOptionB.setText("") self.txtOptionC.setText("") self.txtConvo.setText(self.textVisible) #self.intOptionCount = 2 self.selectedResponse = 0 self.i = 0 self.convoResponseSelected = False taskMgr.add(self.gameConvoUpdate, "GameConvoUpdate") def convoGetOptionCount(self,npcId,depth): if self.convoCheckBranch(npcId,depth+".A") and self.convoCheckBranch(npcId,depth+".B") and self.convoCheckBranch(npcId,depth+".C"): return 3 elif self.convoCheckBranch(npcId,depth+".A") and self.convoCheckBranch(npcId,depth+".B"): return 2 elif self.convoCheckBranch(npcId,depth+".A"): return 1 else: return 0 def getIndent(self, value, increment, limit): if (value + increment >= limit): return limit else: return value+increment def setupKeyboard(self): self.accept("arrow_down", self.convoOptionDown) self.accept("arrow_up", self.convoOptionUp) self.accept("enter",self.convoOptionSelect) def convoOptionUp(self): self.indent = 0.0 if self.selectedResponse == 0: self.selectedResponse = self.intOptionCount-1 elif self.selectedResponse > 0: self.selectedResponse -= 1 def convoOptionDown(self): self.indent = 0.0 if self.selectedResponse < self.intOptionCount-1: self.selectedResponse += 1 elif self.selectedResponse == self.intOptionCount-1: self.selectedResponse = 0 def convoOptionSelect(self): if self.selectedResponse == 0: self.convoDepth += ".A.1" elif self.selectedResponse == 1: self.convoDepth += ".B.1" elif self.selectedResponse == 2: self.convoDepth += ".C.1" self.convoResponseSelected = True
class HUD(DirectObject): #TODO: Preload all images def __init__(self, base, player): self.base = base self.player = player self.heartmax = 160 self.heartmin = 80 self.heartbpm = self.heartmin self.heartaccum = 0 self.last = 0 self.heartbasis = 'assets/images/heartbeat2-%d.png' self.heartframe = 0 self.heartimage = OnscreenImage(self.heartbasis % (1,), scale=(0.1,0.1,0.1), pos=(-1,0, 0.8)) self.keyimage = OnscreenImage('assets/images/key.png', scale=(0.08,0.08,0.08), pos=(-1,0,0.60)) self.keyimage.setTransparency(TransparencyAttrib.MAlpha) self.text = OnscreenText('', pos=(-0.8, 0.8), scale=0.05, fg=(0,1,0,1), bg=(0,0,0,0)) self.hasKey = False self.base.taskMgr.add(self.update, 'hud') def __del__(self): self.base = None self.player = None self.heartimage.destroy() self.text.destroy() def update(self, task): if self.player.isAlive(): elapsed = task.time - self.last self.last = task.time # After a certain point, it should be cleared # Fear must also increase heartbeat f, b = self.player.fear, self.player.breath bpm = 80 + 200 * (0.75 + 0.25 * f) * (1 - b) + 40 * f self.heartaccum += elapsed * (bpm + self.heartbpm) / 2.0 self.heartbpm = bpm self.heartframe = int(8 * self.heartaccum / 60) % 8 self.heartimage.setImage(self.heartbasis % (self.heartframe + 1,)) self.heartimage.setTransparency(TransparencyAttrib.MAlpha) #TODO: Update parameters heartampl = self.heartmax - self.heartmin if self.heartbpm < self.heartmax: s = float(self.heartbpm - self.heartmin) / heartampl self.text.setFg((s,1,0,1)) else: s = float(self.heartbpm - self.heartmax) / heartampl self.text.setFg((1,1-s,0,1)) self.text.setText('%d BPM' % (self.heartbpm,)) else: self.text.setFg((1,1,1,1)) self.text.setText('0 BPM') #TODO: send a 'death' event and, possibly, play back a nice heart stopping animation #return task.done if (self.hasKey): self.keyimage.show() else: self.keyimage.hide() self.heartimage.show() self.text.show() return task.cont def hide(self): self.heartimage.hide() self.text.hide() def setKey(self, key): self.hasKey = key