Video Game Design and Coding

Week 12 packet

Student instructions, tasks, and checks. The answer key is not in this packet. Links stay as text. This page does not embed a clip or run a file.

# Week 12 — Narrative quality and evidence

## VGC-L056 — Write choices that communicate intent

**Objective:** Align button labels with the action they perform.

**Time:** 55 minutes

### Instruction
A choice label is a promise about what the player will do. “Ask about the map” should not unexpectedly destroy the key. Ambiguity can be part of a story, but hiding basic control meaning is usually an interface defect. Revise each choice with a verb and object. Keep passages concise enough to fit the screen. Use your own writing or clearly attributed permitted material; replacing a famous character’s name does not make copied dialogue original.

### Worked example
A long passage can become two short passages connected by Continue. This preserves readable text instead of shrinking the font until the interface technically fits.

### Assignment
1. Revise every choice label to communicate an action.
2. Compare two before/after labels and explain how the new wording reduces uncertainty.

### Acceptance checks
- Each label matches its actual state change or destination.
- No passage copies copyrighted prose without permission.

**Submit:** Revised story text and two label comparisons.

**Question:** What makes a choice label misleading?

**Support:** Keep one complete path working while editing text. Limit the story to six to ten passages until every link is tested. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Add a nonessential lore branch that does not block either ending. Document one changed parameter, your prediction, and the observed effect; optional, not required for completion.

**Optional free reading:** [Godot step by step: nodes, scenes, GDScript, input, signals](https://docs.godotengine.org/en/stable/getting_started/step_by_step/index.html)

---

## VGC-L057 — Test text at different sizes

**Objective:** Use wrapping and layout constraints for readable interfaces.

**Time:** 55 minutes

### Instruction
Text is content with variable length. Increase the default font size in the theme or individual controls, then inspect the longest passage. Containers and anchors should allow text to grow without covering buttons. Test both a 640 by 360 window and a larger window. Do not solve overflow only by shrinking text until it is unreadable. If needed split a passage into two pages with a clear Continue action and an explicit new passage ID.

### Worked example
A long passage can become two short passages connected by Continue. This preserves readable text instead of shrinking the font until the interface technically fits.

Scaffold C-DIALOGUE — Data-driven branching dialogue without personal data
Setup: Create Control root Dialogue with VBoxContainer Box containing Label Line and Buttons A and B. Set Box anchors to full rect; Line autowrap if needed. Attach script to Dialogue. Data keys are fictional scene IDs. All three screens can fit in 640x360. This is a self-contained example; replace story text in the assignment.
```gdscript
extends Control
var page: String = "start"
var story: Dictionary = {
    "start": {"text": "Two paths enter the archive.", "choices": [["Ask the keeper", "keeper"], ["Read a sign", "sign"]]},
    "keeper": {"text": "The keeper offers a map.", "choices": [["Return", "start"]]},
    "sign": {"text": "Quiet room to the east.", "choices": [["Return", "start"]]}
}
func _ready() -> void:
    $Box/A.pressed.connect(_choose.bind(0))
    $Box/B.pressed.connect(_choose.bind(1))
    _show()
func _show() -> void:
    var entry: Dictionary = story[page]
    $Box/Line.text = entry["text"]
    var buttons: Array[Button] = [$Box/A, $Box/B]
    for i in range(buttons.size()):
        buttons[i].visible = i < entry["choices"].size()
        if buttons[i].visible:
            buttons[i].text = entry["choices"][i][0]
    $Box/A.grab_focus()
func _choose(index: int) -> void:
    var choices: Array = story[page]["choices"]
    if index < 0 or index >= choices.size():
        return
    page = str(choices[index][1])
    _show()
```

### Assignment
1. Test the longest passage at normal and enlarged text sizes.
2. Repair one overflow using layout, shorter original prose or an extra page.

### Acceptance checks
- No required action is clipped.
- Reading order stays clear at both window sizes.

**Submit:** Four-condition layout checklist and updated UI project.

**Question:** Why is tiny text a poor universal fix for overflow?

**Support:** Keep one complete path working while editing text. Limit the story to six to ten passages until every link is tested. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Add a nonessential lore branch that does not block either ending. Document one changed parameter, your prediction, and the observed effect; optional, not required for completion.

**Optional free reading:** [Godot step by step: nodes, scenes, GDScript, input, signals](https://docs.godotengine.org/en/stable/getting_started/step_by_step/index.html)

---

## VGC-L058 — Separate decisions from presentation

**Objective:** Refactor story conditions so they can be tested without clicking.

**Time:** 55 minutes

### Instruction
A pure helper returns an answer from inputs without changing the scene. gate_open is easier to test than a button handler that reads labels, changes inventory and transitions pages at once. Extract one story decision into a helper with explicit parameters. Keep mutation in the caller after the helper returns. This separation makes future interface changes safer: a new button layout should not require rewriting the lock rule. Run the same truth-table tests after refactoring.

### Worked example
A long passage can become two short passages connected by Continue. This preserves readable text instead of shrinking the font until the interface technically fits.

Scaffold C-PUZZLE — A testable gate rule
Setup: Create Node root GateLab and attach this script. The pure gate_open function does not read input or change a scene. A level can call it with its inventory and lever state. Assertions run on start. For the assignment write additional tests before changing the rule.
```gdscript
extends Node
func gate_open(has_key: bool, lever_on: bool, alarm_on: bool) -> bool:
    return has_key and lever_on and not alarm_on
func _ready() -> void:
    assert(gate_open(true, true, false))
    assert(not gate_open(false, true, false))
    assert(not gate_open(true, false, false))
    assert(not gate_open(true, true, true))
    print("Gate checks passed")
```

Scaffold C-DIALOGUE — Data-driven branching dialogue without personal data
Setup: Create Control root Dialogue with VBoxContainer Box containing Label Line and Buttons A and B. Set Box anchors to full rect; Line autowrap if needed. Attach script to Dialogue. Data keys are fictional scene IDs. All three screens can fit in 640x360. This is a self-contained example; replace story text in the assignment.
```gdscript
extends Control
var page: String = "start"
var story: Dictionary = {
    "start": {"text": "Two paths enter the archive.", "choices": [["Ask the keeper", "keeper"], ["Read a sign", "sign"]]},
    "keeper": {"text": "The keeper offers a map.", "choices": [["Return", "start"]]},
    "sign": {"text": "Quiet room to the east.", "choices": [["Return", "start"]]}
}
func _ready() -> void:
    $Box/A.pressed.connect(_choose.bind(0))
    $Box/B.pressed.connect(_choose.bind(1))
    _show()
func _show() -> void:
    var entry: Dictionary = story[page]
    $Box/Line.text = entry["text"]
    var buttons: Array[Button] = [$Box/A, $Box/B]
    for i in range(buttons.size()):
        buttons[i].visible = i < entry["choices"].size()
        if buttons[i].visible:
            buttons[i].text = entry["choices"][i][0]
    $Box/A.grab_focus()
func _choose(index: int) -> void:
    var choices: Array = story[page]["choices"]
    if index < 0 or index >= choices.size():
        return
    page = str(choices[index][1])
    _show()
```

### Assignment
1. Extract one condition into a pure function.
2. Call it from the interface and from an assertion test.

### Acceptance checks
- The helper does not change any state.
- UI and standalone tests agree for the same inputs.

**Submit:** Refactored story project and helper tests.

**Question:** Can a pure function read and alter a hidden global inventory?

**Support:** Keep one complete path working while editing text. Limit the story to six to ten passages until every link is tested. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Add a nonessential lore branch that does not block either ending. Document one changed parameter, your prediction, and the observed effect; optional, not required for completion.

**Optional free reading:** [Godot step by step: nodes, scenes, GDScript, input, signals](https://docs.godotengine.org/en/stable/getting_started/step_by_step/index.html)

---

## VGC-L059 — Evaluate a narrative playtest

**Objective:** Separate a reader’s interpretation from a software failure.

**Time:** 55 minutes

### Instruction
During a private test, ask the player to think aloud only if comfortable. Record whether they understand the goal, notice the key, interpret the gate feedback and reach an ending. A player choosing an ending you did not prefer is not automatically a bug. A button going to a nonexistent page is a software failure. A clue that nobody understands is a design issue. Classify findings so the fix matches the cause rather than rewriting the entire story after one unexpected choice.

### Worked example
A long passage can become two short passages connected by Continue. This preserves readable text instead of shrinking the font until the interface technically fits.

### Assignment
1. Run a local test or structured self-test with four observation questions.
2. Classify three findings as code, design, content or preference.

### Acceptance checks
- Each classification cites observed evidence.
- No personal player information is collected.

**Submit:** Anonymous narrative-test report and two prioritized revisions.

**Question:** Is choosing the less cheerful ending necessarily a defect?

**Support:** Keep one complete path working while editing text. Limit the story to six to ten passages until every link is tested. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Add a nonessential lore branch that does not block either ending. Document one changed parameter, your prediction, and the observed effect; optional, not required for completion.

**Optional free reading:** [Godot step by step: nodes, scenes, GDScript, input, signals](https://docs.godotengine.org/en/stable/getting_started/step_by_step/index.html)

---

## VGC-L060 — Submit Archive Lantern

**Objective:** Demonstrate a complete, original and testable interactive narrative.

**Time:** 55 minutes

### Instruction
Finalize six to ten original passages, two endings, one stateful reward and a gate whose logic has an exhaustive truth table. Include a keyboard-accessible restart path and a readme describing the fictional premise, controls and hint system. Submit the source rather than only a recording of one route. Your evidence should show both a successful path and a blocked condition that provides useful feedback. State any remaining limitations, such as no screen-reader integration, instead of claiming universal accessibility.

### Worked example
A long passage can become two short passages connected by Continue. This preserves readable text instead of shrinking the font until the interface technically fits.

Scaffold C-DIALOGUE — Data-driven branching dialogue without personal data
Setup: Create Control root Dialogue with VBoxContainer Box containing Label Line and Buttons A and B. Set Box anchors to full rect; Line autowrap if needed. Attach script to Dialogue. Data keys are fictional scene IDs. All three screens can fit in 640x360. This is a self-contained example; replace story text in the assignment.
```gdscript
extends Control
var page: String = "start"
var story: Dictionary = {
    "start": {"text": "Two paths enter the archive.", "choices": [["Ask the keeper", "keeper"], ["Read a sign", "sign"]]},
    "keeper": {"text": "The keeper offers a map.", "choices": [["Return", "start"]]},
    "sign": {"text": "Quiet room to the east.", "choices": [["Return", "start"]]}
}
func _ready() -> void:
    $Box/A.pressed.connect(_choose.bind(0))
    $Box/B.pressed.connect(_choose.bind(1))
    _show()
func _show() -> void:
    var entry: Dictionary = story[page]
    $Box/Line.text = entry["text"]
    var buttons: Array[Button] = [$Box/A, $Box/B]
    for i in range(buttons.size()):
        buttons[i].visible = i < entry["choices"].size()
        if buttons[i].visible:
            buttons[i].text = entry["choices"][i][0]
    $Box/A.grab_focus()
func _choose(index: int) -> void:
    var choices: Array = story[page]["choices"]
    if index < 0 or index >= choices.size():
        return
    page = str(choices[index][1])
    _show()
```

Scaffold C-PUZZLE — A testable gate rule
Setup: Create Node root GateLab and attach this script. The pure gate_open function does not read input or change a scene. A level can call it with its inventory and lever state. Assertions run on start. For the assignment write additional tests before changing the rule.
```gdscript
extends Node
func gate_open(has_key: bool, lever_on: bool, alarm_on: bool) -> bool:
    return has_key and lever_on and not alarm_on
func _ready() -> void:
    assert(gate_open(true, true, false))
    assert(not gate_open(false, true, false))
    assert(not gate_open(true, false, false))
    assert(not gate_open(true, true, true))
    print("Gate checks passed")
```

### Assignment
1. Package the narrative project, graph, truth table and readme.
2. Demonstrate one full path and one unsolved gate state from a restored copy.

### Acceptance checks
- Both endings and Restart work.
- Teacher can inspect original story data and gate logic.

**Submit:** Archive Lantern source artifact, graph, eight-row truth table and test log; optional narrated route.

**Question:** Which evidence distinguishes code correctness from story quality?

**Support:** Keep one complete path working while editing text. Limit the story to six to ten passages until every link is tested. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Add a nonessential lore branch that does not block either ending. Document one changed parameter, your prediction, and the observed effect; optional, not required for completion.

**Optional free reading:** [Godot step by step: nodes, scenes, GDScript, input, signals](https://docs.godotengine.org/en/stable/getting_started/step_by_step/index.html)

---