class Text(Billboard): def __init__(self, name, nodePath, offset, text, stencilId, scale=0.025000000000000001, *args, **kwargs): Billboard.__init__(self, name, nodePath, *args, **kwargs) self.setBin('fixed', 110) self.scale = scale self.textNode = OnscreenText(text=text, fg=Vec4(0, 0, 0, 1), scale=scale, shadow=Vec4(0, 0, 0, 0), mayChange=True, font=PiratesGlobals.getPirateFont()) self.textNode.detachNode() sNode = self.attachNewNode('stencil') sNode.setY(-offset) self.textNode.instanceTo(sNode) sNode.setDepthTest(False) def setBold(self, bold): self.textNode.setShadow(Vec4(0, 0, 0, bold)) def setTextScale(self, scale): self.textNode.setScale(self.scale * scale)
class Text(Billboard): def __init__(self, name, nodePath, offset, text, stencilId, scale = 0.025000000000000001, *args, **kwargs): Billboard.__init__(self, name, nodePath, *args, **kwargs) self.setBin('fixed', 110) self.scale = scale self.textNode = OnscreenText(text = text, fg = Vec4(0, 0, 0, 1), scale = scale, shadow = Vec4(0, 0, 0, 0), mayChange = True, font = PiratesGlobals.getPirateFont()) self.textNode.detachNode() sNode = self.attachNewNode('stencil') sNode.setY(-offset) self.textNode.instanceTo(sNode) sNode.setDepthTest(False) def setBold(self, bold): self.textNode.setShadow(Vec4(0, 0, 0, bold)) def setTextScale(self, scale): self.textNode.setScale(self.scale * scale)
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 GameTextBox(DirectObject, NodePath): '''游戏文本显示器类 Main displayer of current game text. 继承自Panda3D的DirectFram Attributes: currentText: A list that includes current game text (see sogal_text's recordedText) currentSpeaker: A string that represents the speaker textFont: The font of the text properties: Properties of the text box. ''' def __init__(self): ''' Constructor ''' self.currentText = [] self.currentSpeaker = "" self.newText = None self.textfont = None self.properties = copy.deepcopy(base.getStyle('textbox')) self._normal_speakerLabel = None self._normal_textLabel = None self._large_label = None self._frame = None self._textArrow = None self.__namescale = None NodePath.__init__(self, 'GameTextBox') self.reparentTo(aspect2d) self.reload() def presave(self): runtime_data.RuntimeData.current_text = [self.currentText, self.currentSpeaker] def reload(self): if runtime_data.RuntimeData.gameTextBox_properties: #this creates an reference self.properties = runtime_data.RuntimeData.gameTextBox_properties else: runtime_data.RuntimeData.gameTextBox_properties = self.properties self.applyStyle() if runtime_data.RuntimeData.current_text: if runtime_data.RuntimeData.current_text[0]: self.currentText = copy.copy(runtime_data.RuntimeData.current_text[0]) if self.currentTextLabel: self.currentTextLabel.loadRecordedText(runtime_data.RuntimeData.current_text[0]) if runtime_data.RuntimeData.current_text[1]: self.currentSpeaker = runtime_data.RuntimeData.current_text[1] if self._normal_speakerLabel: self._normal_speakerLabel.setText(runtime_data.RuntimeData.current_text[1]) def reloadTheme(self): self.properties = copy.deepcopy(base.getStyle('textbox')) runtime_data.RuntimeData.gameTextBox_properties = self.properties self.applyStyle() def showArrow(self): if self._textArrow: if self.currentTextLabel: ''' self._textArrow.setPos(self._frame.getWidth()/2-self.properties['arrow_rightspace'], 0, self.currentTextLabel.textNode.getLowerRight3d()[2]-0.03) ''' apos = self._frame.getRelativePoint(self.currentTextLabel, self.currentTextLabel.getEndPos()) apos = (self._frame.getWidth()/2-self.properties['arrow_rightspace'], 0, apos[2]) self._textArrow.setPos(apos) else: self._textArrow.setPos(0,0,0) self._textArrow.show() def hideArrow(self): if self._textArrow: self._textArrow.hide() def quickFinish(self): '''Finish the current text typer quickly ''' if self.currentTextLabel: self.currentTextLabel.quickFinish() def destroyElements(self): self.currentText = [] self.currentSpeaker = None self.newText = '' if self._textArrow: self._textArrow.removeNode() self._textArrow = None if self._normal_textLabel: self._normal_textLabel.destroy() self._normal_textLabel = None if self._normal_speakerLabel: self._normal_speakerLabel.destroy() self._normal_speakerLabel = None if self._frame: self._frame.destroy() self._frame = None if self._large_label: self._large_label.destroy() self._large_label = None def destroy(self, *args, **kwargs): if self.currentTextLabel: self.currentTextLabel.destroy if self._frame: self._frame.destroy() self._frame = None def clearText(self): '''make current text empty''' self.currentText = [] self.currentSpeaker = None self.newText = '' if self.currentTextLabel: self.currentTextLabel.clear() if self._currentStyle == GameTextBoxStyle.Normal and self._normal_speakerLabel: self._normal_speakerLabel.setText('') def pushText(self, text, speaker = None, continuous = False, text_speed = None, fadein = None, rate = 1.0,read = False): '''添加文字 进行判断并改变文字 parameters: speaker: A string contains the speaker's name. (None means no speaker) read: see if the text is already read ''' if self.currentTextLabel and self.currentTextLabel.isWaiting(): self.currentTextLabel.quickFinish() #The text is necessary if not text: return text = text.rstrip('\n') text_speed = (text_speed or base.getStyle('textbox')['text_speed'] or runtime_data.game_settings['text_speed']) * rate if fadein is None: fadein = base.getStyle('textbox')['text_fadein_duration'] fadein_style = base.getStyle('textbox')['text_fadein_style'] if self._currentStyle == GameTextBoxStyle.Normal: if not continuous: self.currentTextLabel.clear() elif self._currentStyle == GameTextBoxStyle.Large: if not continuous and self.currentTextLabel.hasContent(): self.currentTextLabel.appendText('\n') #Inserting an empty line if continuous: #When continuous, ignore the speaker speaker = None #self.currentSpeaker = '' else: self.currentSpeaker = speaker if self._currentStyle == GameTextBoxStyle.Normal: if not continuous: if speaker: self._normal_speakerLabel.setText(self.currentSpeaker) #TODO: use SogalText else: self._normal_speakerLabel.setText(' ') elif self._currentStyle == GameTextBoxStyle.Large: if speaker: self.currentTextLabel.appendText(self.currentSpeaker,custom = True, newLine = True, textScale = self.__namescale) self.newText = text #This is *very* useful safeprint(self.newText) if not read: self.currentTextLabel.appendText(self.newText, speed = text_speed , newLine = (not continuous) , fadein = fadein, fadeinType = fadein_style) else: self.currentTextLabel.appendText(self.newText, speed = text_speed , newLine = (not continuous) , fadein = fadein, fadeinType = fadein_style, custom = True, fg = self.properties['read_fg']) self.currentText = self.currentTextLabel.getCopiedText() #TODO: FADING TEXT AND TYPER AGAIN _currentStyle = None @property def currentStyle(self): '''Style of this box ''' return self._currentStyle @property def currentTextLabel(self): '''current text label ''' if self._currentStyle == GameTextBoxStyle.Normal: return self._normal_textLabel elif self._currentStyle == GameTextBoxStyle.Large: return self._large_label def applyStyle(self): '''套用风格 Apply style setting. override this to apply your own style ''' # 窝才想起来这是引用不是浅拷贝……所以构造函数中运行这个就能同步runtime_data了lol # if runtime_data.RuntimeData.gameTextBox_properties: # self.properties = runtime_data.RuntimeData.gameTextBox_properties # else: runtime_data.RuntimeData.gameTextBox_properties = self.properties self.destroyElements() if self.properties.has_key('style'): self.setTextBoxStyle(self.properties['style']) else: self.setTextBoxStyle('normal') st = self._currentStyle if st == GameTextBoxStyle.Normal: height = self.properties['normal_height'] width = self.properties['normal_width'] self._frame = DirectFrame( parent = self, frameSize = (-width/2.0,width/2.0,-height/2.0,height/2.0), frameColor = self.properties['background_color'], ) if self.currentSpeaker: speaker = self.currentSpeaker else: speaker = '' self._normal_speakerLabel = OnscreenText(parent = self._frame , text = speaker , font = base.textFont , fg = self.properties['foreground_color'] , mayChange = True # @UndefinedVariable , align = TextNode.ALeft#@UndefinedVariable , scale = self.properties['normal_name_scale'] ) self._normal_textLabel = SogalText(parent = self._frame, font = base.textFont, fg = self.properties['foreground_color'], scale = self.properties['normal_text_scale'], shadow = (0.1,0.1,0.1,0.5), pos = (-width/2.0 + self.properties['normal_text_pos'][0], 0, height/2.0 + self.properties['normal_text_pos'][1]), wordwrap = self.properties['normal_text_wrap'] ) self.setPos(self.properties['normal_pos'][0],0,self.properties['normal_pos'][1]) self._normal_speakerLabel.setPos(-width/2.0 + self.properties['normal_name_pos'][0], height/2.0 + self.properties['normal_name_pos'][1]) self._normal_speakerLabel.setShadow((0.1,0.1,0.1,0.5)) self._normal_textLabel.textMaker.setTabWidth(1.0) elif st == GameTextBoxStyle.Large: self.__namescale = self.properties['large_name_scale']/self.properties['large_text_scale'] height = self.properties['large_height'] width = self.properties['large_width'] self._frame = DirectFrame( parent = self, frameSize = (-width/2.0,width/2.0,-height/2.0,height/2.0), frameColor = self.properties['background_color'], ) self._large_label = SogalText(parent = self._frame, font = base.textFont, fg = self.properties['foreground_color'], scale = self.properties['large_text_scale'], shadow = (0.1,0.1,0.1,0.5), pos = (-width/2.0 + self.properties['large_text_pos'][0], 0, height/2.0 + self.properties['large_text_pos'][1]), wordwrap = self.properties['large_text_wrap'] ) self.setPos(self.properties['large_pos'][0],0,self.properties['large_pos'][1]) self._large_label.textMaker.setTabWidth(1.0) #generate an arrow after text arrow = loader.loadModel('models/text_arrow/text_arrow') # @UndefinedVariable arrow.reparentTo(self._frame) arrow.setColor(self.properties['arrow_color']) arrow.setScale(self.properties['arrow_scale']) width = 2.0 if self._currentStyle == GameTextBoxStyle.Normal: width = self.properties['normal_width'] elif self._currentStyle == GameTextBoxStyle.Large: width = self.properties['large_width'] self._textArrow = arrow self._textArrow.hide() def setTextBoxStyle(self, style): if style.strip() == 'normal': self._currentStyle = GameTextBoxStyle.Normal elif style.strip() == 'large': self._currentStyle = GameTextBoxStyle.Large else: safeprint('Unknown style: ' + str(style)) self.properties['style'] = style def paragraphSparator(self): #if self._currentStyle == GameTextBoxStyle.Large: self.clearText() def setTextBoxProperty(self, propname, value): runtime_data.RuntimeData.gameTextBox_properties[propname] = value def applyTextBoxProperties(self): self.applyStyle() def getIsWaitingForText(self): is_waiting = False if self.currentTextLabel: is_waiting = self.currentTextLabel.isWaiting() return is_waiting def getIsWaiting(self): '''Inherited from GameTextBoxBase Get whether the text typer is unfinished ''' return self.getIsWaitingForText()
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 GameTextBox(DirectObject, NodePath): '''游戏文本显示器类 Main displayer of current game text. 继承自Panda3D的DirectFram Attributes: currentText: A list that includes current game text (see sogal_text's recordedText) currentSpeaker: A string that represents the speaker textFont: The font of the text properties: Properties of the text box. ''' def __init__(self): ''' Constructor ''' self.currentText = [] self.currentSpeaker = "" self.newText = None self.textfont = None self.properties = copy.deepcopy(base.getStyle('textbox')) self._normal_speakerLabel = None self._normal_textLabel = None self._large_label = None self._frame = None self._textArrow = None self.__namescale = None NodePath.__init__(self, 'GameTextBox') self.reparentTo(aspect2d) self.reload() def presave(self): runtime_data.RuntimeData.current_text = [ self.currentText, self.currentSpeaker ] def reload(self): if runtime_data.RuntimeData.gameTextBox_properties: #this creates an reference self.properties = runtime_data.RuntimeData.gameTextBox_properties else: runtime_data.RuntimeData.gameTextBox_properties = self.properties self.applyStyle() if runtime_data.RuntimeData.current_text: if runtime_data.RuntimeData.current_text[0]: self.currentText = copy.copy( runtime_data.RuntimeData.current_text[0]) if self.currentTextLabel: self.currentTextLabel.loadRecordedText( runtime_data.RuntimeData.current_text[0]) if runtime_data.RuntimeData.current_text[1]: self.currentSpeaker = runtime_data.RuntimeData.current_text[1] if self._normal_speakerLabel: self._normal_speakerLabel.setText( runtime_data.RuntimeData.current_text[1]) def reloadTheme(self): self.properties = copy.deepcopy(base.getStyle('textbox')) runtime_data.RuntimeData.gameTextBox_properties = self.properties self.applyStyle() def showArrow(self): if self._textArrow: if self.currentTextLabel: ''' self._textArrow.setPos(self._frame.getWidth()/2-self.properties['arrow_rightspace'], 0, self.currentTextLabel.textNode.getLowerRight3d()[2]-0.03) ''' apos = self._frame.getRelativePoint( self.currentTextLabel, self.currentTextLabel.getEndPos()) apos = (self._frame.getWidth() / 2 - self.properties['arrow_rightspace'], 0, apos[2]) self._textArrow.setPos(apos) else: self._textArrow.setPos(0, 0, 0) self._textArrow.show() def hideArrow(self): if self._textArrow: self._textArrow.hide() def quickFinish(self): '''Finish the current text typer quickly ''' if self.currentTextLabel: self.currentTextLabel.quickFinish() def destroyElements(self): self.currentText = [] self.currentSpeaker = None self.newText = '' if self._textArrow: self._textArrow.removeNode() self._textArrow = None if self._normal_textLabel: self._normal_textLabel.destroy() self._normal_textLabel = None if self._normal_speakerLabel: self._normal_speakerLabel.destroy() self._normal_speakerLabel = None if self._frame: self._frame.destroy() self._frame = None if self._large_label: self._large_label.destroy() self._large_label = None def destroy(self, *args, **kwargs): if self.currentTextLabel: self.currentTextLabel.destroy if self._frame: self._frame.destroy() self._frame = None def clearText(self): '''make current text empty''' self.currentText = [] self.currentSpeaker = None self.newText = '' if self.currentTextLabel: self.currentTextLabel.clear() if self._currentStyle == GameTextBoxStyle.Normal and self._normal_speakerLabel: self._normal_speakerLabel.setText('') def pushText(self, text, speaker=None, continuous=False, text_speed=None, fadein=None, rate=1.0, read=False): '''添加文字 进行判断并改变文字 parameters: speaker: A string contains the speaker's name. (None means no speaker) read: see if the text is already read ''' if self.currentTextLabel and self.currentTextLabel.isWaiting(): self.currentTextLabel.quickFinish() #The text is necessary if not text: return text = text.rstrip('\n') text_speed = (text_speed or base.getStyle('textbox')['text_speed'] or runtime_data.game_settings['text_speed']) * rate if fadein is None: fadein = base.getStyle('textbox')['text_fadein_duration'] fadein_style = base.getStyle('textbox')['text_fadein_style'] if self._currentStyle == GameTextBoxStyle.Normal: if not continuous: self.currentTextLabel.clear() elif self._currentStyle == GameTextBoxStyle.Large: if not continuous and self.currentTextLabel.hasContent(): self.currentTextLabel.appendText( '\n') #Inserting an empty line if continuous: #When continuous, ignore the speaker speaker = None #self.currentSpeaker = '' else: self.currentSpeaker = speaker if self._currentStyle == GameTextBoxStyle.Normal: if not continuous: if speaker: self._normal_speakerLabel.setText( self.currentSpeaker) #TODO: use SogalText else: self._normal_speakerLabel.setText(' ') elif self._currentStyle == GameTextBoxStyle.Large: if speaker: self.currentTextLabel.appendText(self.currentSpeaker, custom=True, newLine=True, textScale=self.__namescale) self.newText = text #This is *very* useful safeprint(self.newText) if not read: self.currentTextLabel.appendText(self.newText, speed=text_speed, newLine=(not continuous), fadein=fadein, fadeinType=fadein_style) else: self.currentTextLabel.appendText(self.newText, speed=text_speed, newLine=(not continuous), fadein=fadein, fadeinType=fadein_style, custom=True, fg=self.properties['read_fg']) self.currentText = self.currentTextLabel.getCopiedText() #TODO: FADING TEXT AND TYPER AGAIN _currentStyle = None @property def currentStyle(self): '''Style of this box ''' return self._currentStyle @property def currentTextLabel(self): '''current text label ''' if self._currentStyle == GameTextBoxStyle.Normal: return self._normal_textLabel elif self._currentStyle == GameTextBoxStyle.Large: return self._large_label def applyStyle(self): '''套用风格 Apply style setting. override this to apply your own style ''' # 窝才想起来这是引用不是浅拷贝……所以构造函数中运行这个就能同步runtime_data了lol # if runtime_data.RuntimeData.gameTextBox_properties: # self.properties = runtime_data.RuntimeData.gameTextBox_properties # else: runtime_data.RuntimeData.gameTextBox_properties = self.properties self.destroyElements() if self.properties.has_key('style'): self.setTextBoxStyle(self.properties['style']) else: self.setTextBoxStyle('normal') st = self._currentStyle if st == GameTextBoxStyle.Normal: height = self.properties['normal_height'] width = self.properties['normal_width'] self._frame = DirectFrame( parent=self, frameSize=(-width / 2.0, width / 2.0, -height / 2.0, height / 2.0), frameColor=self.properties['background_color'], ) if self.currentSpeaker: speaker = self.currentSpeaker else: speaker = '' self._normal_speakerLabel = OnscreenText( parent=self._frame, text=speaker, font=base.textFont, fg=self.properties['foreground_color'], mayChange=True # @UndefinedVariable , align=TextNode.ALeft #@UndefinedVariable , scale=self.properties['normal_name_scale']) self._normal_textLabel = SogalText( parent=self._frame, font=base.textFont, fg=self.properties['foreground_color'], scale=self.properties['normal_text_scale'], shadow=(0.1, 0.1, 0.1, 0.5), pos=(-width / 2.0 + self.properties['normal_text_pos'][0], 0, height / 2.0 + self.properties['normal_text_pos'][1]), wordwrap=self.properties['normal_text_wrap']) self.setPos(self.properties['normal_pos'][0], 0, self.properties['normal_pos'][1]) self._normal_speakerLabel.setPos( -width / 2.0 + self.properties['normal_name_pos'][0], height / 2.0 + self.properties['normal_name_pos'][1]) self._normal_speakerLabel.setShadow((0.1, 0.1, 0.1, 0.5)) self._normal_textLabel.textMaker.setTabWidth(1.0) elif st == GameTextBoxStyle.Large: self.__namescale = self.properties[ 'large_name_scale'] / self.properties['large_text_scale'] height = self.properties['large_height'] width = self.properties['large_width'] self._frame = DirectFrame( parent=self, frameSize=(-width / 2.0, width / 2.0, -height / 2.0, height / 2.0), frameColor=self.properties['background_color'], ) self._large_label = SogalText( parent=self._frame, font=base.textFont, fg=self.properties['foreground_color'], scale=self.properties['large_text_scale'], shadow=(0.1, 0.1, 0.1, 0.5), pos=(-width / 2.0 + self.properties['large_text_pos'][0], 0, height / 2.0 + self.properties['large_text_pos'][1]), wordwrap=self.properties['large_text_wrap']) self.setPos(self.properties['large_pos'][0], 0, self.properties['large_pos'][1]) self._large_label.textMaker.setTabWidth(1.0) #generate an arrow after text arrow = loader.loadModel( 'models/text_arrow/text_arrow') # @UndefinedVariable arrow.reparentTo(self._frame) arrow.setColor(self.properties['arrow_color']) arrow.setScale(self.properties['arrow_scale']) width = 2.0 if self._currentStyle == GameTextBoxStyle.Normal: width = self.properties['normal_width'] elif self._currentStyle == GameTextBoxStyle.Large: width = self.properties['large_width'] self._textArrow = arrow self._textArrow.hide() def setTextBoxStyle(self, style): if style.strip() == 'normal': self._currentStyle = GameTextBoxStyle.Normal elif style.strip() == 'large': self._currentStyle = GameTextBoxStyle.Large else: safeprint('Unknown style: ' + str(style)) self.properties['style'] = style def paragraphSparator(self): #if self._currentStyle == GameTextBoxStyle.Large: self.clearText() def setTextBoxProperty(self, propname, value): runtime_data.RuntimeData.gameTextBox_properties[propname] = value def applyTextBoxProperties(self): self.applyStyle() def getIsWaitingForText(self): is_waiting = False if self.currentTextLabel: is_waiting = self.currentTextLabel.isWaiting() return is_waiting def getIsWaiting(self): '''Inherited from GameTextBoxBase Get whether the text typer is unfinished ''' return self.getIsWaitingForText()