Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion code/cfile/cfile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ cf_pathtype Pathtypes[CF_MAX_PATH_TYPES] = {
{ CF_TYPE_INTEL_ANIMS, "data" DIR_SEPARATOR_STR "intelanims", ".pcx .ani .eff .tga .jpg .png .dds", CF_TYPE_DATA },
{ CF_TYPE_SCRIPTS, "data" DIR_SEPARATOR_STR "scripts", ".lua .lc .fnl", CF_TYPE_DATA },
{ CF_TYPE_FICTION, "data" DIR_SEPARATOR_STR "fiction", ".txt", CF_TYPE_DATA },
{ CF_TYPE_FREDDOCS, "data" DIR_SEPARATOR_STR "freddocs", ".html", CF_TYPE_DATA }
{ CF_TYPE_FREDDOCS, "data" DIR_SEPARATOR_STR "freddocs", ".html .qch", CF_TYPE_DATA }
};
// clang-format on

Expand Down
77 changes: 75 additions & 2 deletions qtfred/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ SET(QT5_INSTALL_ROOT "" CACHE PATH

list(APPEND CMAKE_PREFIX_PATH "${QT5_INSTALL_ROOT}")

find_package(Qt5 COMPONENTS Widgets OpenGL REQUIRED)
find_package(Qt5 COMPONENTS Widgets OpenGL Help REQUIRED)

include(source_groups.cmake)

Expand Down Expand Up @@ -56,7 +56,7 @@ set(CMAKE_MAP_IMPORTED_CONFIG_FASTDEBUG Release Debug)
target_link_libraries(qtfred
PUBLIC
code
Qt5::Widgets Qt5::OpenGL)
Qt5::Widgets Qt5::OpenGL Qt5::Help)

include(CreateLaunchers)
create_target_launcher(qtfred
Expand All @@ -72,6 +72,66 @@ INSTALL(
)
COPY_FILES_TO_TARGET(qtfred)

# --- QtFRED built-in help ---
get_target_property(_QTFRED_QMAKE Qt5::qmake IMPORTED_LOCATION)
execute_process(
COMMAND "${_QTFRED_QMAKE}" -query QT_INSTALL_BINS
OUTPUT_VARIABLE _QTFRED_QT_BINS
OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(
COMMAND "${_QTFRED_QMAKE}" -query QT_INSTALL_LIBS
OUTPUT_VARIABLE _QTFRED_QT_LIBS
OUTPUT_STRIP_TRAILING_WHITESPACE)
find_program(QHELPGENERATOR_EXECUTABLE
NAMES qhelpgenerator
HINTS "${_QTFRED_QT_BINS}"
REQUIRED)

set(QTFRED_HELP_QHP "${CMAKE_CURRENT_SOURCE_DIR}/help-src/doc/qtfred.qhp")
set(QTFRED_HELP_QCH "${CMAKE_CURRENT_BINARY_DIR}/qtfred_help.qch")

file(GLOB_RECURSE QTFRED_HELP_SOURCES
"${CMAKE_CURRENT_SOURCE_DIR}/help-src/doc/*.html"
"${CMAKE_CURRENT_SOURCE_DIR}/help-src/doc/*.css"
"${CMAKE_CURRENT_SOURCE_DIR}/help-src/doc/*.qhp")

# qhelpgenerator is a Qt application and needs a platform plugin at build time.
# On headless Linux CI the Qt platform plugins fail to dlopen because the Qt
# shared libs are not in LD_LIBRARY_PATH for dlopen-loaded plugins (DT_RUNPATH
# in the main binary does not propagate to dlopen). Pass the Qt lib dir and
# force the offscreen (no-display) platform explicitly.
add_custom_command(
OUTPUT "${QTFRED_HELP_QCH}"
COMMAND ${CMAKE_COMMAND} -E env
"QT_QPA_PLATFORM=offscreen"
"LD_LIBRARY_PATH=${_QTFRED_QT_LIBS}"
"${QHELPGENERATOR_EXECUTABLE}" "${QTFRED_HELP_QHP}" -o "${QTFRED_HELP_QCH}"
DEPENDS ${QTFRED_HELP_SOURCES}
COMMENT "Compiling QtFRED help (qtfred_help.qch)"
VERBATIM)

add_custom_target(qtfred_help
DEPENDS "${QTFRED_HELP_QCH}"
SOURCES ${QTFRED_HELP_SOURCES})

add_dependencies(qtfred qtfred_help)

source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/help-src"
PREFIX "help-src"
FILES ${QTFRED_HELP_SOURCES})

add_custom_command(TARGET qtfred_help POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory "$<TARGET_FILE_DIR:qtfred>/help"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${QTFRED_HELP_QCH}"
"$<TARGET_FILE_DIR:qtfred>/help/qtfred_help.qch"
VERBATIM)

install(FILES "${QTFRED_HELP_QCH}"
DESTINATION ${BINARY_DESTINATION}/help
COMPONENT "qtFRED")
# --- end QtFRED built-in help ---

enable_clang_tidy(qtfred)

if (WIN32)
Expand Down Expand Up @@ -120,6 +180,19 @@ if (WIN32)
DESTINATION ${BINARY_DESTINATION}/platforms
COMPONENT "qtFRED"
)

# Qt Help requires the SQLite SQL driver
set(qsqlite_path "${QT_INSTALL_PLUGINS}/sqldrivers/qsqlite$<$<CONFIG:Debug>:d>.dll")

add_custom_command(TARGET qtfred
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${qsqlite_path}" "$<TARGET_FILE_DIR:qtfred>/sqldrivers/qsqlite$<$<CONFIG:Debug>:d>.dll"
VERBATIM)

install(FILES ${qsqlite_path}
DESTINATION ${BINARY_DESTINATION}/sqldrivers
COMPONENT "qtFRED"
)
elseif(FSO_BUILD_APPIMAGE)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/AppRun.in" "${CMAKE_CURRENT_BINARY_DIR}/AppRun.gen" @ONLY)
file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/AppRun-$<CONFIG>"
Expand Down
62 changes: 62 additions & 0 deletions qtfred/help-src/doc/concepts/ai.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>AI - QtFRED Help</title>
<link rel="stylesheet" type="text/css" href="../css/help.css"/>
</head>
<body>
<nav class="breadcrumb"><a href="../index.html">QtFRED Help</a> &rsaquo; Concepts &rsaquo; AI</nav>
<h1>AI</h1>
<p>Ship AI behavior in FSO is controlled by three layered systems: the
<strong>AI profile</strong>, the <strong>AI class</strong>, and
<strong>AI goals and orders</strong>. Each layer handles a different aspect of
behavior and they work together at runtime.</p>

<h2>AI profile</h2>
<p>The AI profile is a mission-wide setting chosen in
<a href="../dialogs/MissionSpecsDialog.html">Mission Specs</a>. It is primarily a
<strong>difficulty scaling system</strong>: it defines per-difficulty-level values
for things like damage multipliers, weapon fire delays, turn time, shield recharge
rates, and aim behavior; controlling how the mission feels across the Very Easy
through Insane difficulty range. Profiles also contain a large set of behavioral
flags that toggle specific AI behaviors on or off.</p>
<p>Profiles are defined in <code>ai_profiles.tbl</code> and missions select from
that list; custom profiles cannot be defined inside the mission file itself. If no
profile is specified, the table's default profile is used. Most missions are fine
with the default.</p>

<h2>AI class</h2>
<p>The AI class is a named skill preset assigned to individual ships. It controls
the skill level of that specific ship within the envelope set by the profile;
how well it aims, how it maneuvers, how it uses afterburners, and so on.</p>
<p>Every ship class has a default AI class defined in its table entry. The
<a href="../dialogs/ShipEditorDialog.html">Ship Editor</a> lets you override that
default for a specific ship instance, and individual turrets can be given their
own AI class in the <a href="../dialogs/ShipWeaponsDialog.html">Weapons</a>
dialog.</p>

<h2>AI goals and orders</h2>
<p>Goals and orders are specific tasks assigned to a ship or wing: attack a
target, guard a ship, dock with a vessel, fly a waypoint path, and so on. There
are two ways to set them:</p>
<ul>
<li><strong>Initial orders</strong> - set in the
<a href="../dialogs/ShipInitialOrdersDialog.html">Initial Orders</a>
dialog. These are the tasks a ship starts the mission with.</li>
<li><strong>Runtime goals</strong> - issued via SEXPs during the mission,
typically from events. These override or supplement initial orders.</li>
</ul>
<p>When a ship has multiple active goals, it pursues them in priority order.
Priority is a value from 1 to 200 where higher values take precedence. Goals
issued by the player via the comm menu use priorities in the 90–100 range, so
goals with a priority above 89 may outrank player orders.</p>

<h2>How the layers interact</h2>
<p>Think of it as: the <strong>profile</strong> sets the rules of the game, the
<strong>class</strong> sets how skilled the ship is within those rules, and the
<strong>goals and orders</strong> tell the ship what to do. A high-skill AI
class given a low-priority goal can still be overridden by a player order; a
locked-in high-priority goal will hold regardless of player input.</p>
</body>
</html>
65 changes: 65 additions & 0 deletions qtfred/help-src/doc/concepts/campaign-flow.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Campaign Flow - QtFRED Help</title>
<link rel="stylesheet" type="text/css" href="../css/help.css"/>
</head>
<body>
<nav class="breadcrumb"><a href="../index.html">QtFRED Help</a> &rsaquo; Concepts &rsaquo; Campaign Flow</nav>
<h1>Campaign Flow</h1>
<p>A campaign is a sequence of missions linked together in the
<a href="../dialogs/CampaignEditorDialog.html">Campaign Editor</a>. The campaign
file controls which mission plays next, branches the story based on outcomes, and
carries state between missions through goals and variables.</p>

<h2>How missions end</h2>
<p>A mission ends when the player reaches the debrief screen. There are two
distinct end conditions that affect what state is saved:</p>
<table>
<tr><th>Condition</th><th>When it occurs</th><th>What is saved</th></tr>
<tr><td>Mission completed</td><td>The player accepts the debriefing after
flying the mission.</td><td>Variables with <em>Save on Mission
Completed</em> persistence, plus all goal states.</td></tr>
<tr><td>Mission closed</td><td>The player exits the mission by any means,
including failure or replaying.</td><td>Variables with <em>Save on Mission
Close</em> persistence.</td></tr>
</table>

<h2>Goal states</h2>
<p>Every mission goal ends in one of three states: complete, failed, or
incomplete. The campaign SEXP tree reads these states using
<code>goal-true</code> and <code>goal-false</code> operators to decide which
mission to branch to next. This is how outcomes carry forward; whether an
important ship survived, whether a bonus objective was met, and so on.</p>
<p>Goals do not automatically determine success or failure. The campaign SEXPs
are what interpret goal states and produce outcomes. A mission where all primary
goals are complete can still branch to a "defeat" mission if the campaign logic
is written that way.</p>

<h2>Variables across missions</h2>
<p>Variables set during a mission only persist into subsequent missions if their
persistence is configured accordingly. See the
<a href="../dialogs/VariablesAndContainersDialog.html">Variables &amp; Containers</a>
dialog for the persistence options. Variables saved to the player file
(<em>Eternal</em>) persist for the lifetime of the player profile, even across
different campaigns.</p>

<h2>Branching</h2>
<p>The campaign editor arranges missions as nodes connected by branches. Each
branch has a SEXP condition. After a mission completes, the engine evaluates the
outgoing branches in order and follows the first one whose condition is true. If
no branch condition is met, the player is returned to the main menu. To end the
campaign properly (playing the end cutscene and triggering campaign completion)
use the <code>end-campaign</code> SEXP.</p>
<p>Common branching patterns include:</p>
<ul>
<li>Checking whether a primary goal succeeded to route to a victory or
defeat mission.</li>
<li>Checking whether an optional goal was completed to unlock bonus
missions.</li>
<li>Reading a variable set earlier in the campaign to take a story
branch.</li>
</ul>
</body>
</html>
14 changes: 14 additions & 0 deletions qtfred/help-src/doc/concepts/custom-data.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Custom Data - QtFRED Help</title>
<link rel="stylesheet" type="text/css" href="../css/help.css"/>
</head>
<body>
<nav class="breadcrumb"><a href="../index.html">QtFRED Help</a> &rsaquo; Concepts &rsaquo; Custom Data</nav>
<h1>Custom Data</h1>

<div class="stub-notice">Full documentation for this concept is not yet written.</div>
</body>
</html>
59 changes: 59 additions & 0 deletions qtfred/help-src/doc/concepts/iff.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>IFF &amp; Teams - QtFRED Help</title>
<link rel="stylesheet" type="text/css" href="../css/help.css"/>
</head>
<body>
<nav class="breadcrumb"><a href="../index.html">QtFRED Help</a> &rsaquo; Concepts &rsaquo; IFF &amp; Teams</nav>
<h1>IFF &amp; Teams</h1>
<p>IFF stands for <em>Identification Friend or Foe</em>. Every ship in a mission
belongs to a team, and every team has a defined relationship with every other team.
Those relationships determine how ships behave toward one another: whether they
attack, ignore, or treat each other as allies.</p>

<h2>Team relationships</h2>
<p>Team relationships are defined in <code>iff_defs.tbl</code>, not in the mission
file. The common defaults are:</p>
<table>
<tr><th>Team</th><th>Typical role</th></tr>
<tr><td>Friendly</td><td>Player's side. Ships the player can target for
orders and that friendly AI protects.</td></tr>
<tr><td>Hostile</td><td>Enemy. Ships that actively attack Friendly
ships.</td></tr>
<tr><td>Neutral</td><td>Neither side. Not attacked unless attacked
first.</td></tr>
<tr><td>Unknown</td><td>Unidentified. Treated as neutral until scanned or
otherwise identified.</td></tr>
<tr><td>Traitor</td><td>Formerly friendly ships that have turned hostile.
Attacked by all sides.</td></tr>
</table>
<p>Mods can define additional teams and custom relationships between them. The
teams available in your mission depend on what <code>iff_defs.tbl</code> provides
for the loaded mod.</p>

<h2>What team membership affects</h2>
<ul>
<li><strong>AI targeting</strong> - ships attack any ship they have a hostile
relationship with and avoid attacking allies.</li>
<li><strong>Escort and guard behavior</strong> - AI set to guard a ship will
also engage enemies threatening it.</li>
<li><strong>Comm menu</strong> - the player can only issue orders to ships on
the Friendly team.</li>
<li><strong>HUD</strong> - target brackets, colors, and the hostile/friendly
indicators on the radar all derive from IFF relationships.</li>
<li><strong>SEXPs</strong> — many SEXP operators accept a team argument to
act on all ships of a given team at once.</li>
</ul>

<h2>Changing team at runtime</h2>
<p>A ship's team can be changed during the mission using SEXPs. This is how
defection scenarios work: reassigning a ship to the Traitor or Hostile team
immediately causes former allies to engage it.</p>

<div class="note">The Team field in the <a href="../dialogs/ShipEditorDialog.html">Ship Editor</a>
sets the team at mission start. Runtime changes are made through SEXPs and are
not reflected back in the editor.</div>
</body>
</html>
56 changes: 56 additions & 0 deletions qtfred/help-src/doc/concepts/messages.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Messages - QtFRED Help</title>
<link rel="stylesheet" type="text/css" href="../css/help.css"/>
</head>
<body>
<nav class="breadcrumb"><a href="../index.html">QtFRED Help</a> &rsaquo; Concepts &rsaquo; Messages</nav>
<h1>Messages</h1>
<p>Messages are the in-mission communications the player sees and hears. These
include wingmate callouts, command briefings, enemy taunts. Each message is
defined once in the mission's global message list and can be triggered by any event.</p>

<h2>Message anatomy</h2>
<table>
<tr><th>Field</th><th>Description</th></tr>
<tr><td>Name</td><td>Internal identifier used to reference the message from
events and SEXPs.</td></tr>
<tr><td>Text</td><td>The text displayed in the message box.</td></tr>
<tr><td>Audio file</td><td>The voice file played with the message.</td></tr>
<tr><td>Persona</td><td>The voice persona associated with this message, used
to select the appropriate voice variant.</td></tr>
<tr><td>Sender</td><td>The ship that sends the message.</td></tr>
</table>

<h2>Priority</h2>
<p>Each message has a priority - <strong>Low</strong>, <strong>Normal</strong>, or
<strong>High</strong> - that controls how it interacts with other messages playing
at the same time.</p>
<table>
<tr><th>Priority</th><th>Behavior</th></tr>
<tr><td>Low</td><td>Queued behind any currently playing or queued messages.
Will not interrupt anything.</td></tr>
<tr><td>Normal</td><td>Queued behind messages of equal or higher priority.
Interrupts low-priority messages.</td></tr>
<tr><td>High</td><td>Interrupts any lower-priority message currently
playing.</td></tr>
</table>
<p>Within the same priority level, messages play in the order they were
triggered.</p>

<h2>Triggering messages</h2>
<p>Messages are sent by events in the
<a href="../dialogs/MissionEventsDialog.html">Mission Events</a> dialog using a
send-message SEXP. The same message can be triggered by multiple events, and the
same event can send multiple messages.</p>

<h2>Personas</h2>
<p>Personas are defined in the game tables and control the voice set used for a
message. Assigning a persona to both a message and a ship creates a consistent
"voice" for that character across the mission. A ship with a Command persona
is eligible to serve as the Command sender in
<a href="../dialogs/MissionSpecsDialog.html">Mission Specs</a>.</p>
</body>
</html>
Loading
Loading