Module 17

Point-and-Click Adventure

Puzzle-solving through object interaction and conversation — every item is a question; every combination is an answer.

"I'm selling these fine leather jackets."

Prerequisites

ModuleWhat You Used From It
Module 01 - PongBasic game loop, rendering, and state management. Point-and-click adventures are mechanically simple but architecturally demanding.

Week 1: History & Design Theory

The Origin

The Secret of Monkey Island (1990), designed by Ron Gilbert and Tim Schafer at LucasArts, perfected the point-and-click adventure formula by solving the genre's greatest problem: unfair death. Sierra On-Line's earlier adventures (King's Quest, 1984) punished players with frequent, often unpredictable deaths and dead-end states where the game became unwinnable without the player knowing. Gilbert's philosophy, codified in his "Why Adventure Games Suck" manifesto, insisted that the player should never die and should never be able to reach a dead end. Monkey Island implemented this by designing every puzzle as a contained logical problem with discoverable solutions, using humor to make failed attempts entertaining rather than punishing, and building an inventory-combination system where the fun was in experimentation. The SCUMM engine (Script Creation Utility for Maniac Mansion) gave designers a verb-based interaction system — Look At, Pick Up, Use, Talk To — that made every screen a dense web of discoverable interactions.

How the Genre Evolved

Myst (1993): Cyan Worlds' Rand and Robyn Miller stripped away the verb interface, inventory, and dialogue entirely, replacing them with environmental puzzles embedded in hauntingly beautiful pre-rendered worlds. Players clicked to move between static viewpoints and manipulated mechanisms directly. Myst proved that point-and-click adventure did not require inventory or conversation — pure environmental observation and logical deduction could sustain an entire game.

Disco Elysium (2019): ZA/UM's masterwork reimagined the adventure game through the lens of a tabletop RPG. Instead of combining physical objects, the player combined ideas in an internalized "thought cabinet." Every conversation was a potential puzzle, with 24 distinct skills functioning as inner voices that provided clues, opinions, and sometimes unreliable advice. It demonstrated that the core loop of point-and-click — observe, gather information, combine knowledge to progress — could be applied to dialogue and character psychology rather than physical inventory puzzles.

What Makes Point-and-Click "Great"

The core design insight of the point-and-click adventure is lateral thinking as gameplay. The genre asks the player to look at the world not as it is but as it could be — a rubber chicken is not just a rubber chicken but a potential tool when combined with a pulley. This trains a specific cognitive skill: the ability to hold multiple unrelated objects in mind and ask "what if these go together?" The best adventure games design puzzles where the solution, once discovered, feels obvious in retrospect ("of course you use the monkey as a wrench") — this retrospective clarity is the "aha!" moment that drives the entire genre.

The Essential Mechanic

Combining found objects and information to solve environmental puzzles — the player observes, collects, reasons about connections, and applies items or knowledge to overcome obstacles.


Week 2: Build the MVP

What You're Building

A 3-5 room adventure game with an inventory system, at least one item-combination puzzle, a dialog tree with a single NPC, and a puzzle dependency chain where solving one puzzle unlocks the next. The player clicks to interact with hotspots, collects items, and combines them to progress.

This module is 2D. No engine is required.

Core Concepts (Must Implement)

1. Inventory System with Item Combination

The player carries a collection of items represented as objects with properties (name, description, icon, use-cases). Items can be used on hotspots in the world or combined with each other in the inventory to produce new items.

class Item:
    def __init__(self, id, name, description, icon):
        self.id = id
        self.name = name
        self.description = description
        self.icon = icon

class Inventory:
    def __init__(self):
        self.items = {}  # id -> Item

    def add(self, item):
        self.items[item.id] = item

    def remove(self, item_id):
        del self.items[item_id]

    def has(self, item_id):
        return item_id in self.items

    def try_combine(self, item_a_id, item_b_id):
        key = frozenset([item_a_id, item_b_id])
        if key in COMBINATION_RECIPES:
            result = COMBINATION_RECIPES[key]
            self.remove(item_a_id)
            self.remove(item_b_id)
            self.add(result)
            return result
        return None

COMBINATION_RECIPES = {
    frozenset(["rope", "hook"]): Item("grapple", "Grappling Hook",
        "A hook tied to a rope. Could reach high places.", "grapple.png"),
    frozenset(["key_half_a", "key_half_b"]): Item("full_key", "Complete Key",
        "Both halves joined together.", "full_key.png"),
}

Why it matters: The inventory is the player's toolkit. Every puzzle in the game is ultimately solved by having the right item and applying it in the right place. The combination system multiplies puzzle possibilities exponentially — N items yield N*(N-1)/2 potential combinations.

Demo: Inventory Combination Puzzle

Click on highlighted objects in the scene to pick them up. Items appear in the inventory bar at the bottom. Click an inventory item to select it (yellow border), then click another item to try combining them. Use combined items on the locked chest to solve the puzzle!

2. Puzzle Dependency Graph

The game's puzzles form a directed acyclic graph (DAG) where each puzzle has prerequisites (items, flags, or other puzzles). Designing this graph up front ensures puzzles are solvable in a valid order and prevents dead ends.

puzzle_graph = {
    "get_rope": {
        requires: [],
        grants: ["rope"],
        description: "Pick up rope from the dock"
    },
    "talk_to_fisherman": {
        requires: [],
        grants: ["flag:fisherman_talked"],
        description: "Learn that the fisherman lost his hook"
    },
    "get_hook": {
        requires: ["flag:fisherman_talked"],
        grants: ["hook"],
        description: "Fisherman gives you his spare hook"
    },
    "make_grapple": {
        requires: ["rope", "hook"],
        grants: ["grapple"],
        description: "Combine rope + hook = grappling hook"
    },
    "climb_tower": {
        requires: ["grapple"],
        grants: ["flag:reached_tower_top"],
        description: "Use grappling hook to reach the tower top"
    },
    "open_gate": {
        requires: ["key"],
        grants: ["flag:game_complete"],
        description: "Use key on the locked gate — victory!"
    }
}

Why it matters: The puzzle dependency graph is the game's blueprint. Without it, designers risk creating unsolvable states, circular dependencies, or puzzles where the player has no idea what to do next.

3. Dialog Trees

Conversations with NPCs are represented as a graph of dialog nodes. Each node contains NPC text and a list of player response options. Selecting a response may lead to another dialog node, set a game state flag, give the player an item, or end the conversation.

dialog_fisherman = {
    "start": {
        npc_text: "Ahoy! I'd be fishing, but I lost my best hook overboard.",
        options: [
            { text: "That's too bad.", next: "sympathy" },
            { text: "Do you have a spare?", next: "spare_hook" },
            { text: "Goodbye.", next: null }
        ]
    },
    "sympathy": {
        npc_text: "Aye, it's been a rough week. Say, you look resourceful...",
        options: [
            { text: "Do you have a spare hook?", next: "spare_hook" },
            { text: "I should go.", next: null }
        ]
    },
    "spare_hook": {
        npc_text: "Matter of fact, I do. Here, take it.",
        options: [
            { text: "Thanks!", next: null }
        ],
        on_enter: [
            { action: "give_item", item: "hook" },
            { action: "set_flag", flag: "fisherman_talked", value: true }
        ]
    }
}

Why it matters: Dialog is the primary way adventure games deliver narrative, character, and puzzle clues. The tree structure lets designers create conversations that feel responsive to the player's curiosity while ensuring that essential information is always reachable.

Demo: Branching Dialog Tree

Click on dialog options to navigate the conversation. The graph on the right updates in real-time showing which nodes you have visited (green), your current node (yellow), and unvisited nodes (gray). Flags set by the conversation appear at the bottom. Choices can unlock or lock future options!

4. Hotspot Interaction System

Each scene contains clickable hotspots — regions of the screen that the player can interact with. Hotspots have a position, a bounding area, and responses for different interaction types (look, use, use-item-on). The cursor changes to indicate when it is over a hotspot.

class Hotspot:
    def __init__(self, id, name, bounds, interactions):
        self.id = id
        self.name = name
        self.bounds = bounds          # {x, y, width, height}
        self.interactions = interactions
        self.active = true

    def contains(self, mouse_x, mouse_y):
        b = self.bounds
        return (b.x <= mouse_x <= b.x + b.width and
                b.y <= mouse_y <= b.y + b.height)

def handle_click(mouse_x, mouse_y, current_scene, selected_item):
    for hotspot in current_scene.hotspots:
        if not hotspot.active or not hotspot.contains(mouse_x, mouse_y):
            continue
        if selected_item:
            use_item = hotspot.interactions.get("use_item", {})
            response = use_item.get(selected_item.id, use_item.get("default"))
        else:
            response = hotspot.interactions.get("look")
        execute_response(response)
        return
    deselect_item()

Why it matters: Hotspots are the player's primary interface with the game world. They transform a static background image into an interactive space. The cursor feedback is critical — the player must be able to discover what is interactive without clicking blindly on every pixel.

5. Scene / Room Management

The game is organized into discrete scenes (rooms), each with its own background image, hotspots, NPCs, and entry/exit points. A scene manager handles loading scenes, running transitions between them, and maintaining per-scene state.

class SceneManager:
    def __init__(self):
        self.scenes = {}
        self.current_scene = null
        self.transition_state = "none"
        self.fade_alpha = 0.0

    def change_scene(self, target_id):
        self.transition_state = "fading_out"
        self.next_scene_id = target_id

    def update(self, dt):
        if self.transition_state == "fading_out":
            self.fade_alpha += dt * FADE_SPEED
            if self.fade_alpha >= 1.0:
                self.current_scene = self.scenes[self.next_scene_id]
                self.transition_state = "fading_in"
        elif self.transition_state == "fading_in":
            self.fade_alpha -= dt * FADE_SPEED
            if self.fade_alpha <= 0.0:
                self.fade_alpha = 0.0
                self.transition_state = "none"

Why it matters: Scene management is the architecture that holds the entire game together. Clean scene management makes the game trivially extensible — adding a new room means defining a new data object, not modifying core logic.

6. Game State Flags

A centralized flag system tracks everything the player has done, seen, and said. Flags are boolean or string values keyed by name. Hotspots, dialog options, and puzzle logic all read from and write to this shared state.

class GameStateFlags:
    def __init__(self):
        self.flags = {}

    def set_flag(self, name, value=true):
        self.flags[name] = value

    def get_flag(self, name, default=false):
        return self.flags.get(name, default)

    def check_condition(self, condition):
        if " AND " in condition:
            parts = condition.split(" AND ")
            return all(self.check_condition(p.strip()) for p in parts)
        if " OR " in condition:
            parts = condition.split(" OR ")
            return any(self.check_condition(p.strip()) for p in parts)
        if condition.startswith("!"):
            return not self.check_flag(condition[1:])
        return self.check_flag(condition)

Why it matters: Flags are the memory of the game world. They are how the game tracks cause and effect across rooms, conversations, and time. Without a centralized flag system, tracking what the player has done becomes a tangled mess of ad-hoc variables.


Stretch Goals

MVP Spec

FeatureRequired
3-5 scenes with background imagesYes
Clickable hotspots with look/use responsesYes
Inventory UI with item collectionYes
At least one item combination puzzleYes
Use-item-on-hotspot interactionYes
At least one NPC with dialog treeYes
Scene transitions (fade or cut)Yes
Game state flags driving conditional contentYes
Puzzle dependency chain of 4+ stepsYes
Context-sensitive cursorYes
Character walking animationStretch
Verb bar interfaceStretch
Conditional dialog optionsStretch
Hint systemStretch

Deliverable

Submit your playable adventure game with source code and a puzzle dependency graph diagram showing all puzzles as nodes, their prerequisites as edges, and the items/flags each puzzle grants. Include a short write-up (300-500 words) answering: How did you design your puzzle chain to be solvable without a walkthrough? What clues did you embed in dialog, item descriptions, and environmental details to guide the player toward solutions?


Analogies by Background

These analogies map game dev concepts to patterns you already know.

For Backend Developers

Game Dev ConceptBackend Analogy
Inventory with item combinationA key-value store with composite key lookups — items are values, combinations are queries against a recipe table, producing new entries
Puzzle dependency graphA task DAG (like Airflow or Makefile targets) — each task has prerequisites that must complete before it can execute
Dialog treesA state machine for request handling — each state presents a response and a set of valid transitions, with side effects triggered on state entry
Hotspot interaction systemA URL router with method handling — each hotspot is a route, each interaction type is an HTTP method
Scene / room managementMicroservice orchestration — each scene is an independent service with its own state, and the scene manager is the API gateway
Game state flagsA feature flag service (LaunchDarkly, etc.) — a centralized boolean store that all services query to determine behavior

For Frontend Developers

Game Dev ConceptFrontend Analogy
Inventory with item combinationShopping cart with bundle logic — items in the cart can be combined into bundles, producing a new line item
Puzzle dependency graphComponent dependency tree — components (puzzles) can only render (solve) when their required props (items/flags) are provided
Dialog treesA multi-step wizard/form — each step shows content and options, the selected option determines the next step
Hotspot interaction systemEvent delegation on a composite UI — clickable regions within an image map, each with hover states and click handlers
Scene / room managementClient-side routing — each scene is a route/page, the scene manager is the router
Game state flagsGlobal application state (Redux/Zustand) — a centralized store that components subscribe to

For Data / ML Engineers

Game Dev ConceptData / ML Analogy
Inventory with item combinationFeature engineering — raw features (items) are combined through defined transformations (recipes) to produce derived features
Puzzle dependency graphA data pipeline DAG (Airflow, dbt) — each node is a transformation with defined inputs
Dialog treesA decision tree classifier — each node splits on a player choice, leaf nodes produce outcomes
Hotspot interaction systemLabeled training data — each hotspot is an annotated region in an image with associated metadata
Scene / room managementDataset partitioning — each scene is a partition of the total game data, loaded independently
Game state flagsExperiment tracking metadata — a centralized log of which experiments (actions) have been run

Discussion Questions

  1. Fair puzzles vs. obscure puzzles: Classic adventure games were notorious for illogical puzzles (the "cat hair mustache" problem). How do you design puzzles that are challenging but fair? What is the role of the puzzle dependency graph in ensuring the player always has enough information?
  2. The dead-end problem: Ron Gilbert's manifesto argued that adventure games should never create unwinnable states. How does your puzzle dependency graph guarantee this? Could a game intentionally use dead ends as a design tool?
  3. Dialog as puzzle: In games like Disco Elysium, dialog itself is the puzzle. How does this differ from using dialog purely as a clue-delivery system?
  4. The hint economy: When a player is stuck, they will either consult a walkthrough (leaving your game) or quit. How would you design an in-game hint system that helps stuck players without spoiling satisfaction?