Changeset 24979

Timestamp:
Mar 2, 2021, 4:43:44 PM (3 years ago)
Author:
wraitii
Message:

Implement Single-Player campaigns - Barebones tutorial campaign included.

This implements necessary tooling to create a simple SP campaign.
The architecture is intended to be easily extensible in the future.

'Campaign Run' contains the metadata of a campaign, e.g. maps played/won. It's saved in the user folder under saves/campaigns/*.0adcampaign
Campaign templates are JSON files in campaigns/

Campaigns can specify which Menu interface they will use. This is intended to allow more complex layouts/presentation.
For now, a simple list interface is provided. This allows making campaigns without any fancy art required (and effectively mimics AoE1's campaign interface).

The behaviour on game end is also intended to be extensible, supporting things such as carrying over units between scenarios - for now, it simply records won games.

GameSetup is not available for now - scenarios are triggered with the settings defined in the map/default settings. Improving on this requires refactoring the gamesetup further.

The load/save game page has been extended slightly to support showing/hiding campaign games (campaign gamed are saved under saves/ directly, there is no strong motivation to do otherwise at this point)

Closes #4387

Differential Revision: https://code.wildfiregames.com/D11

Location:
ps/trunk
Files:
31 added
17 edited

Legend:

Unmodified
Added
Removed
  • ps/trunk/binaries/data/mods/mod/gui/common/modern/styles.xml

    r24439 r24979  
    3131        sprite="ModernDarkBoxGoldNoTop"
    3232        sprite_selectarea="ModernDarkBoxWhite"
    33         sprite_heading="ModernDarkBoxGoldNoBottom"
    3433        textcolor="white"
    3534        textcolor_selected="white"
  • ps/trunk/binaries/data/mods/mod/gui/modmod/modmod.xml

    r24439 r24979  
    102102
    103103            <object name="modsEnabledList"
    104                 style="ModernList"
     104                style="ModernList"
    105105                type="olist"
    106106                size="0 25 100%-32-2-2 100%"
  • ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/GameSettingsControl.js

    r24900 r24979  
    107107        if (initData && initData.map && initData.mapType)
    108108        {
    109             Object.defineProperty(this, "autostart", {
    110                 "value": true,
    111                 "writable": false,
    112                 "configurable": false
    113             });
     109            if (initData.autostart)
     110                Object.defineProperty(this, "autostart", {
     111                    "value": true,
     112                    "writable": false,
     113                    "configurable": false
     114                });
    114115
    115116            // TODO: Fix g_GameAttributes, g_GameAttributes.settings,
     
    117118            // copy over each attribute individually when receiving
    118119            // settings from the server or the local file.
    119             g_GameAttributes = {
    120                 "mapType": initData.mapType,
    121                 "map": initData.map
    122             };
     120            g_GameAttributes = initData;
    123121
    124122            this.updateGameAttributes();
  • ps/trunk/binaries/data/mods/public/gui/loadgame/SavegameList.js

    r22985 r24979  
    99class SavegameList
    1010{
    11     constructor()
     11    constructor()
    1212    {
    1313        this.savedGamesMetadata = [];
    1414        this.selectionChangeHandlers = [];
     15
     16
     17
     18
     19
     20
     21
    1522
    1623        this.gameSelection = Engine.GetGUIObjectByName("gameSelection");
     
    6875
    6976        if (this.compatibilityFilter.checked)
    70             savedGames = savedGames.filter(game => this.isCompatibleSavegame(game.metadata, engineInfo));
     77            savedGames = savedGames.filter(game => {
     78                return this.isCompatibleSavegame(game.metadata, engineInfo) &&
     79                this.campaignFilter(game.metadata, this.campaignRun);
     80            });
     81        else if (this.campaignRun)
     82            savedGames = savedGames.filter(game => this.campaignFilter(game.metadata, this.campaignRun));
     83
    7184
    7285        this.gameSelection.enabled = !!savedGames.length;
     
    115128
    116129        let list = this.savedGamesMetadata.map(metadata => {
    117             let isCompatible = this.isCompatibleSavegame(metadata, engineInfo);
     130            let isCompatible = this.isCompatibleSavegame(metadata, engineInfo) &&
     131                               this.campaignFilter(metadata, this.campaignRun);
    118132            return {
    119133                "date": this.generateSavegameDateString(metadata, engineInfo),
     
    143157        else if (this.gameSelection.selected >= this.savedGamesMetadata.length)
    144158            this.gameSelection.selected = this.savedGamesMetadata.length - 1;
     159
     160
     161
     162
     163
     164
     165
     166
     167
    145168    }
    146169
  • ps/trunk/binaries/data/mods/public/gui/loadgame/SavegamePage.js

    r24677 r24979  
    1 /**
    2  * This class architecture is an example of how to use classes
    3  * to encapsulate and to avoid fragmentation and globals.
    4  */
    5 var g_SavegamePage;
    6 
    7 function init(data)
    8 {
    9     g_SavegamePage = new SavegamePage(data);
    10 }
    11 
    121/**
    132 * This class is responsible for loading the affected GUI control classes,
     
    187    constructor(data)
    198    {
    20         this.savegameList = new SavegameList();
     9        this.savegameList = new SavegameList();
    2110
    2211        this.savegameDetails = new SavegameDetails();
     
    4736    }
    4837}
     38
     39
     40
     41
     42
     43
     44
  • ps/trunk/binaries/data/mods/public/gui/pregame/MainMenuItemHandler.js

    r23701 r24979  
    3939            button.caption = item.caption;
    4040            button.tooltip = item.tooltip;
    41             button.enabled = item.enabled === undefined || item.enabled;
     41            button.enabled = item.enabled === undefined || item.enabled;
    4242            button.onPress = this.pressButton.bind(this, item, i);
    4343            button.hidden = false;
  • ps/trunk/binaries/data/mods/public/gui/pregame/MainMenuItems.js

    r24726 r24979  
    1616                "onPress": () => {
    1717                    Engine.SwitchGuiPage("page_gamesetup.xml", {
     18
    1819                        "mapType": "scenario",
    1920                        "map": "maps/tutorials/starting_economy_walkthrough"
     
    6263    },
    6364    {
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
    6475        "caption": translate("Single-player"),
    6576        "tooltip": translate("Start, load, or replay a single-player game."),
     
    7384            },
    7485            {
    75                 "caption": translate("Campaigns"),
    76                 "tooltip": translate("Relive history through historical military campaigns. \\[NOT YET IMPLEMENTED]"),
    77                 "enabled": false
    78             },
    79             {
    8086                "caption": translate("Load Game"),
    8187                "tooltip": translate("Load a saved game."),
    8288                "onPress": () => {
    8389                    Engine.PushGuiPage("page_loadgame.xml");
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
     100
     101
     102
     103
     104
     105
     106
     107
     108
     109
     110
     111
     112
     113
     114
     115
     116
    84117                }
    85118            },
     
    127160                    colorizeHotkey(translate("%(hotkey)s: Launch the multiplayer lobby to join and host publicly visible games and chat with other players."), "lobby") +
    128161                    (Engine.StartXmppClient ? "" : translate("Launch the multiplayer lobby. \\[DISABLED BY BUILD]")),
    129                 "enabled": !!Engine.StartXmppClient,
     162                "enabled": !!Engine.StartXmppClient,
    130163                "hotkey": "lobby",
    131164                "onPress": () => {
  • ps/trunk/binaries/data/mods/public/gui/pregame/mainmenu.xml

    r23093 r24979  
    33
    44    <script directory="gui/common/"/>
     5
    56    <script directory="gui/pregame/"/>
    67    <script directory="gui/pregame/backgrounds/"/>
  • ps/trunk/binaries/data/mods/public/gui/session/MenuButtons.js

    r24215 r24979  
    6262        Engine.PushGuiPage(
    6363            "page_loadgame.xml",
    64             { "savedGameData": getSavedGameData() },
     64            {
     65                "savedGameData": getSavedGameData(),
     66                "campaignRun": g_CampaignSession ? g_CampaignSession.run.filename : null
     67            },
    6568            resumeGame);
    6669    }
  • ps/trunk/binaries/data/mods/public/gui/session/session.js

    r24952 r24979  
    267267    }
    268268
     269
     270
     271
    269272    let mapCache = new MapCache();
    270273    g_Cheats = new Cheats();
     
    525528        Engine.SendUnregisterGame();
    526529
    527     Engine.SwitchGuiPage("page_summary.xml", {
     530    {
    528531        "sim": simData,
    529532        "gui": {
     
    535538            "replaySelectionData": g_ReplaySelectionData
    536539        }
    537     });
     540    };
     541
     542    if (g_GameAttributes.campaignData)
     543    {
     544        let menu = g_CampaignSession.getMenu();
     545        if (g_GameAttributes.campaignData.skipSummary)
     546        {
     547            Engine.SwitchGuiPage(menu);
     548            return;
     549        }
     550        summaryData.campaignData = { "filename": g_GameAttributes.campaignData.run };
     551        summaryData.nextPage = menu;
     552    }
     553
     554    Engine.SwitchGuiPage("page_summary.xml", summaryData);
    538555}
    539556
  • ps/trunk/binaries/data/mods/public/gui/session/session.xml

    r24565 r24979  
    44
    55<script directory="gui/common/"/>
     6
    67<script directory="gui/maps/"/>
    78<script directory="gui/session/"/>
     9
    810<script directory="gui/session/chat/"/>
    911<script directory="gui/session/developer_overlay/"/>
  • ps/trunk/binaries/data/mods/public/gui/summary/summary.js

    r24721 r24979  
    474474            "summarySelection": summarySelection
    475475        });
     476
     477
    476478    else
    477479        Engine.SwitchGuiPage("page_pregame.xml");
  • ps/trunk/binaries/data/mods/public/l10n/.tx/config

    r21911 r24979  
    3030file_filter = <lang>.public-gui-other.po
    3131source_file = public-gui-other.pot
     32
     33
     34
     35
     36
    3237source_lang = en
    3338
  • ps/trunk/binaries/data/mods/public/l10n/messages.json

    r24947 r24979  
    237237                ],
    238238                "options": {
     239
     240
     241
     242
     243
     244
     245
     246
     247
     248
     249
     250
     251
     252
     253
     254
     255
     256
     257
     258
     259
     260
     261
     262
     263
     264
     265
     266
     267
     268
     269
     270
     271
     272
     273
     274
     275
     276
     277
     278
     279
     280
     281
     282
     283
     284
     285
     286
     287
     288
     289
     290
     291
     292
     293
     294
     295
     296
    239297                }
    240298            }
  • ps/trunk/binaries/data/mods/public/simulation/components/GuiInterface.js

    r24967 r24979  
    210210        "mapSettings": InitAttributes.settings
    211211    };
     212
     213
     214
     215
     216
     217
     218
     219
     220
     221
     222
    212223};
    213224
     
    20132024    "GetInitAttributes": 1,
    20142025    "GetReplayMetadata": 1,
     2026
    20152027    "GetRenamedEntities": 1,
    20162028    "ClearRenamedEntities": 1,
  • ps/trunk/source/ps/scripting/JSInterface_VFS.cpp

    r24333 r24979  
    1 /* Copyright (C) 2020 Wildfire Games.
     1/* Copyright (C) 202 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
     
    210210}
    211211
     212
     213
     214
     215
     216
     217
     218
     219
     220
     221
     222
     223
    212224bool JSI_VFS::PathRestrictionMet(ScriptInterface::CmptPrivate* pCmptPrivate, const std::vector<CStrW>& validPaths, const CStrW& filePath)
    213225{
     
    260272    scriptInterface.RegisterFunction<JS::Value, std::wstring, &Script_ReadJSONFile_GUI>("ReadJSONFile");
    261273    scriptInterface.RegisterFunction<void, std::wstring, JS::HandleValue, &WriteJSONFile>("WriteJSONFile");
     274
    262275}
    263276
  • ps/trunk/source/ps/scripting/JSInterface_VFS.h

    r24177 r24979  
    1 /* Copyright (C) 2018 Wildfire Games.
     1/* Copyright (C) 20 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
     
    4848    void WriteJSONFile(ScriptInterface::CmptPrivate* pCmptPrivate, const std::wstring& filePath, JS::HandleValue val1);
    4949
     50
     51
     52
     53
    5054    // Tests whether the current script context is allowed to read from the given directory
    5155    bool PathRestrictionMet(ScriptInterface::CmptPrivate* pCmptPrivate, const std::vector<CStrW>& validPaths, const CStrW& filePath);
Note: See TracChangeset for help on using the changeset viewer.