4 Комити c4216a5677 ... 39f61317f3

Аутор SHA1 Порука Датум
  Jason K. MacDuffie 39f61317f3 open the main menu for bad ending пре 1 месец
  Jason K. MacDuffie 579e772751 start work on puzzle-less levels пре 1 месец
  Jason K. MacDuffie feca43ffa2 add a bad ending пре 1 месец
  Jason K. MacDuffie b69ac272fd add a bad ending пре 1 месец
5 измењених фајлова са 89 додато и 51 уклоњено
  1. 58 41
      data/stories.json
  2. 9 4
      data_manager.py
  3. 11 2
      game_state.py
  4. 5 1
      raw_assets/stories/constellation.ini
  5. 6 3
      util/generate_story.py

+ 58 - 41
data/stories.json

@@ -24,10 +24,10 @@
                 "ending": null,
                 "choices": null,
                 "puzzle": {
-                    "encrypted_text": "FQD UFCJU FEVM RD COEXF IEX. IEX SAVV YCAV, BXUF VAPD FQEUD ODYEJD IEX. A QCTD C SCJDQEXUD YXVV EY FQD OEMADU EY MDFDKFATDU VAPD IEX.",
-                    "decrypted_text": "THE STARS TOLD ME ABOUT YOU. YOU WILL FAIL, JUST LIKE THOSE BEFORE YOU. I HAVE A WAREHOUSE FULL OF THE BODIES OF DETECTIVES LIKE YOU.",
+                    "encrypted_text": "FQD UFCJU FEVM RD COEXF IEX, MDFDKFATD. REJCVU CJD KEHUFCHFVI XHMDJNEAHN KQCHNDU CHM FJCHUYEJRCFAEHU, EKKCUAEHDM OI UXKKDUUYXV KJARDU. A QCTD C SCJDQEXUD YXVV EY FQD OEMADU EY MDFDKFATDU VAPD IEX.",
+                    "decrypted_text": "THE STARS TOLD ME ABOUT YOU, DETECTIVE. MORALS ARE CONSTANTLY UNDERGOING CHANGES AND TRANSFORMATIONS, OCCASIONED BY SUCCESSFUL CRIMES. I HAVE A WAREHOUSE FULL OF THE BODIES OF DETECTIVES LIKE YOU.",
                     "quote": {
-                        "quote": "THE STARS TOLD ME ABOUT YOU. YOU WILL FAIL, JUST LIKE THOSE BEFORE YOU. I HAVE A WAREHOUSE FULL OF THE BODIES OF DETECTIVES LIKE YOU.",
+                        "quote": "THE STARS TOLD ME ABOUT YOU, DETECTIVE. MORALS ARE CONSTANTLY UNDERGOING CHANGES AND TRANSFORMATIONS, OCCASIONED BY SUCCESSFUL CRIMES. I HAVE A WAREHOUSE FULL OF THE BODIES OF DETECTIVES LIKE YOU.",
                         "source": "CONSTELLATION SLAYER"
                     },
                     "key": {
@@ -85,10 +85,10 @@
                 "ending": null,
                 "choices": null,
                 "puzzle": {
-                    "encrypted_text": "EUR CEHWC NPTO MTZ ETT ORBB, VRERFEQJR. Q'JR OHEFURV MTZ EUWTZLU KM OQCRPRV RMRC. Q EUQPN MTZ OQBB IQPV MTZW CHBJHEQTP HE H FWTCCWTHVC.",
-                    "decrypted_text": "THE STARS KNOW YOU TOO WELL, DETECTIVE. I'VE WATCHED YOU THROUGH MY WISENED EYES. I THINK YOU WILL FIND YOUR SALVATION AT A CROSSROADS.",
+                    "encrypted_text": "EUR CEHWC NPTO MTZ ETT ORBB, VRERFEQJR. KHP QC FTPVRKPRV ET GR IWRR. XRWUHXC MTZ OQBB IQPV CHBJHEQTP HE H FWTCCWTHVC.",
+                    "decrypted_text": "THE STARS KNOW YOU TOO WELL, DETECTIVE. MAN IS CONDEMNED TO BE FREE. PERHAPS YOU WILL FIND SALVATION AT A CROSSROADS.",
                     "quote": {
-                        "quote": "THE STARS KNOW YOU TOO WELL, DETECTIVE. I'VE WATCHED YOU THROUGH MY WISENED EYES. I THINK YOU WILL FIND YOUR SALVATION AT A CROSSROADS.",
+                        "quote": "THE STARS KNOW YOU TOO WELL, DETECTIVE. MAN IS CONDEMNED TO BE FREE. PERHAPS YOU WILL FIND SALVATION AT A CROSSROADS.",
                         "source": "CONSTELLATION SLAYER"
                     },
                     "key": {
@@ -140,16 +140,17 @@
                     "Great, just great. Another puzzle from the Constellation Slayer."
                 ],
                 "post_narrative": [
-                    "This time you're sure you understand the message -- \"lower\" means the next murder will be in a basement. You call the department to ensure every basement in Brooklyn is under surveillance."
+                    "This time you're sure you understand the message -- \"lower\" means the next murder will be somewhere underground.",
+                    "On the other hand, is this guy quoting Nietzsche? What a nut job."
                 ],
-                "next_level": "4",
+                "next_level": "5",
                 "ending": null,
                 "choices": null,
                 "puzzle": {
-                    "encrypted_text": "OJO NET WQJXB WQS VLKW CTGGVS HLK MSXSLWQ NET, OSWSPWJRS? MTW LVV WQJXUK LAS MSXSLWQ WQS KWLAK. NET ZTKW KSLAPQ VEHSA LXO VEHSA.",
-                    "decrypted_text": "DID YOU THINK THE LAST PUZZLE WAS BENEATH YOU, DETECTIVE? BUT ALL THINGS ARE BENEATH THE STARS. YOU MUST SEARCH LOWER AND LOWER.",
+                    "encrypted_text": "LVV WQJXUK LAS MSXSLWQ WQS KWLAK, OSWSPWJRS. JI NET ULGS VEXU SXETUQ JXWE LX LMNKK, WQS LMNKK HJVV ULGS MLPB. NET ZTKW KSLAPQ VEHSA LXO OSSCSA JXWE WQS LMNKK.",
+                    "decrypted_text": "ALL THINGS ARE BENEATH THE STARS, DETECTIVE. IF YOU GAZE LONG ENOUGH INTO AN ABYSS, THE ABYSS WILL GAZE BACK. YOU MUST SEARCH LOWER AND DEEPER INTO THE ABYSS.",
                     "quote": {
-                        "quote": "DID YOU THINK THE LAST PUZZLE WAS BENEATH YOU, DETECTIVE? BUT ALL THINGS ARE BENEATH THE STARS. YOU MUST SEARCH LOWER AND LOWER.",
+                        "quote": "ALL THINGS ARE BENEATH THE STARS, DETECTIVE. IF YOU GAZE LONG ENOUGH INTO AN ABYSS, THE ABYSS WILL GAZE BACK. YOU MUST SEARCH LOWER AND DEEPER INTO THE ABYSS.",
                         "source": "CONSTELLATION SLAYER"
                     },
                     "key": {
@@ -191,6 +192,22 @@
             {
                 "id": "4",
                 "index": 13,
+                "name": "Bad Ending",
+                "pre_narrative": [
+                    "It's another late evening, and you're still trying to crack the case of the Constellation Slayer. You head to your apartment in Bay Ridge, settle in, and greet your dog. Suddenly, there\u2019s a knock at the door.",
+                    "You know who it is before you even turn around. You don\u2019t need to hear the footsteps or see the shadow in the hallway. You\u2019ve already lost. There\u2019s no escape, no clever deduction that can change the outcome now.",
+                    "Heart pounding, you turn to face the inevitable. Standing in the doorway is the Constellation Slayer, his eyes gleaming with that same cold, knowing look. The smile on his face is the final piece of the puzzle you never solved.",
+                    "As the light fades, your last conscious thought is the realization that you were just another piece in his game -- a pawn to be sacrificed, now nothing but a memory."
+                ],
+                "post_narrative": null,
+                "next_level": null,
+                "ending": "lose",
+                "choices": null,
+                "puzzle": null
+            },
+            {
+                "id": "5",
+                "index": 14,
                 "name": "Placeholder",
                 "pre_narrative": [
                     "This level does not count."
@@ -200,48 +217,48 @@
                 "ending": "win",
                 "choices": null,
                 "puzzle": {
-                    "encrypted_text": "QB QGB KQ: HJ K FQVFNNKBFGT BKNVKDPN, RFXFVXKBF? XLF NXHAN RD ODX MKOR JF ND. HX XLF AKWLX XKJF, BKAXPF HOR BKVF UKGG YF AFBFHGFR.",
+                    "encrypted_text": "JX JGX AJ: ON A PJTPCCAXPGE XACTAHLC, ZPKPTKAXP? KBP CKOSC ZH UHK VAUZ NP CH. OK KBP SADBK KANP, XASKLP OUZ XATP RAGG YP SPXPOGPZ.",
                     "decrypted_text": "XV XLV IX: AM I EXCESSIVELY VISCIOUS, DETECTIVE? THE STARS DO NOT FIND ME SO. AT THE RIGHT TIME, VIRTUE AND VICE WILL BE REVEALED.",
                     "quote": {
                         "quote": "XV XLV IX: AM I EXCESSIVELY VISCIOUS, DETECTIVE? THE STARS DO NOT FIND ME SO. AT THE RIGHT TIME, VIRTUE AND VICE WILL BE REVEALED.",
                         "source": "CONSTELLATION SLAYER"
                     },
                     "key": {
-                        "A": "H",
+                        "A": "O",
                         "B": "Y",
-                        "C": "V",
-                        "D": "R",
-                        "E": "F",
-                        "F": "M",
-                        "G": "W",
-                        "H": "L",
-                        "I": "K",
-                        "J": "Z",
-                        "K": "C",
+                        "C": "T",
+                        "D": "Z",
+                        "E": "P",
+                        "F": "V",
+                        "G": "D",
+                        "H": "B",
+                        "I": "A",
+                        "J": "F",
+                        "K": "W",
                         "L": "G",
-                        "M": "J",
-                        "N": "O",
-                        "O": "D",
-                        "P": "I",
-                        "Q": "E",
-                        "R": "A",
-                        "S": "N",
-                        "T": "X",
-                        "U": "P",
-                        "V": "B",
-                        "W": "U",
-                        "X": "Q",
-                        "Y": "T",
-                        "Z": "S"
+                        "M": "N",
+                        "N": "U",
+                        "O": "H",
+                        "P": "Q",
+                        "Q": "I",
+                        "R": "S",
+                        "S": "C",
+                        "T": "K",
+                        "U": "L",
+                        "V": "X",
+                        "W": "R",
+                        "X": "J",
+                        "Y": "E",
+                        "Z": "M"
                     },
                     "letter_mappings": {
-                        "N": "S",
-                        "X": "T",
-                        "H": "A",
-                        "A": "R"
+                        "C": "S",
+                        "K": "T",
+                        "O": "A",
+                        "S": "R"
                     }
                 }
             }
         ]
     }
-]
+]

+ 9 - 4
data_manager.py

@@ -24,16 +24,21 @@ class DataManager:
         elif game_state.mode == 'story':
             if 'story_mode' not in data:
                 data['story_mode'] = {}
-            data['story_mode'][game_state.story['id']] = {
-                'level': game_state.level['id'],
-                'level_history': game_state.level_history,
-                'current_game': {
+            if game_state.puzzle is None:
+                current_game = None
+            else:
+                current_game = {
                     'encrypted_text': game_state.puzzle.encrypted_text,
                     'decrypted_text': game_state.puzzle.decrypted_text,
                     'quote': game_state.puzzle.quote,
                     'key': game_state.puzzle.key,
                     'letter_mappings': game_state.puzzle.letter_mappings
                 }
+
+            data['story_mode'][game_state.story['id']] = {
+                'level': game_state.level['id'],
+                'level_history': game_state.level_history,
+                'current_game': current_game
             }
 
         data['user_preferences'] = {

+ 11 - 2
game_state.py

@@ -29,11 +29,20 @@ class GameState:
         if level_data['id'] not in self.level_history['unlocked_levels']:
             self.level_history['unlocked_levels'].append(self.level['id'])
 
-        self.puzzle = Puzzle(level_data['puzzle']['quote'], level_data['puzzle']['key'], level_data['puzzle']['letter_mappings'])
+        if level_data['puzzle'] is not None:
+            self.puzzle = Puzzle(level_data['puzzle']['quote'], level_data['puzzle']['key'], level_data['puzzle']['letter_mappings'])
+            self.screen = 'puzzle'
+        else:
+            self.puzzle = None
+            self.mode = None
+            self.story = None
+            self.level = None
+            self.open_main_menu()
+
         self.dialog_list.append(Dialog([level_data['name']], self))
+
         if not skip_pre_narrative and level_data['pre_narrative'] is not None:
             self.dialog_list.append(Dialog(level_data['pre_narrative'], self))
-        self.screen = 'puzzle'
 
     def open_main_menu(self):
         self.mode = None

+ 5 - 1
raw_assets/stories/constellation.ini

@@ -20,9 +20,13 @@ post_narrative = He's definitely leading you to the next murder. "Crossroads" --
 pre_narrative = A few days later, you're in the office trying to piece together the identity of the killer. You're looking through past cases to see if there could be a connection, when you hear a ring.\nYou already know what the call means. You pick up the phone and learn the location of his next kill: Our Lady of Angels Church in Bay Ridge.\nDamn it! You misinterpreted the meaning of "crossroads" in his last puzzle. You arrive at the church just after dusk. The old-constructed building looms against the dark blue sky. The streets are so quiet, you can hear a pin drop.\nThe expressions of the officers are grim. You don't look forward to seeing the scene.\nImmediately, three smells hit you: burning candle was, aged wood, and the smell of blood. You make your way to the altar, towards the victim's body. His body is nailed to the cross, at the neck. And on the nail, there is a piece of paper with writing scrawled on it.\nGreat, just great. Another puzzle from the Constellation Slayer.
 quote = ALL THINGS ARE BENEATH THE STARS, DETECTIVE. IF YOU GAZE LONG ENOUGH INTO AN ABYSS, THE ABYSS WILL GAZE BACK. YOU MUST SEARCH LOWER AND DEEPER INTO THE ABYSS.
 letter_hints = STAR
-post_narrative = This time you're sure you understand the message -- "lower" means the next murder will be in a basement. You call the department to ensure every basement in Brooklyn is under surveillance.
+post_narrative = This time you're sure you understand the message -- "lower" means the next murder will be somewhere underground.\nOn the other hand, is this guy quoting Nietzsche? What a nut job.
+
+[Bad Ending]
+pre_narrative = It's another late evening, and you're still trying to crack the case of the Constellation Slayer. You head to your apartment in Bay Ridge, settle in, and greet your dog. Suddenly, there’s a knock at the door.\nYou know who it is before you even turn around. You don’t need to hear the footsteps or see the shadow in the hallway. You’ve already lost. There’s no escape, no clever deduction that can change the outcome now.\nHeart pounding, you turn to face the inevitable. Standing in the doorway is the Constellation Slayer, his eyes gleaming with that same cold, knowing look. The smile on his face is the final piece of the puzzle you never solved.\nAs the light fades, your last conscious thought is the realization that you were just another piece in his game -- a pawn to be sacrificed, now nothing but a memory.
 
 [Placeholder]
 pre_narrative = This level does not count.
 quote = XV XLV IX: AM I EXCESSIVELY VISCIOUS, DETECTIVE? THE STARS DO NOT FIND ME SO. AT THE RIGHT TIME, VIRTUE AND VICE WILL BE REVEALED. 
 letter_hints = STAR
+

+ 6 - 3
util/generate_story.py

@@ -23,11 +23,14 @@ def parse_ini_to_json(ini_path):
 
         key = generate_derangement()
         letter_mappings = {}
-        for hint in level_data.get("letter_hints"):
+        for hint in level_data.get("letter_hints", []):
             letter_mappings[key[hint]] = hint
         source = level_data.get("source", global_source)
 
-        puzzle = Puzzle({"quote": level_data.get("quote"), "source": source}, key, letter_mappings)
+        if 'quote' in level_data:
+            puzzle = Puzzle({"quote": level_data.get("quote"), "source": source}, key, letter_mappings)
+        else:
+            puzzle = None
 
         levels.append({
             "id": str(i),
@@ -38,7 +41,7 @@ def parse_ini_to_json(ini_path):
             "next_level": str(i + 1) if (h + 1) < len(level_names) else None,
             "ending": "win" if h == len(level_names) - 1 else None,
             "choices": None,
-            "puzzle": {
+            "puzzle": None if puzzle is None else {
                 "encrypted_text": puzzle.encrypted_text,
                 "decrypted_text": puzzle.decrypted_text,
                 "quote": puzzle.quote,