Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mission system (.missionzip) #3018

Closed
wants to merge 7 commits into from
Closed

Conversation

ohlidalp
Copy link
Member

@ohlidalp ohlidalp commented Feb 12, 2023

Info:
https://forum.rigsofrods.org/threads/mission-system-prototype.3846/

Features:

  • A new mod type is recognized: *.mission - can add terrain objects, procedural roads and races to existing terrain.
  • A new archive type is recognized: *.missionzip
  • Mission types: "race" and "stunt", more to come in the future.
  • To load a mission, use top menubar "Simulation / Load a mission" (opens the Selector UI) or open in-game console and say 'loadmission fileName'
  • To unload a mission, use top menubar "Missions" menu and click the red "X" letter.

New scripts:

  • missions.as - like races.as but for missions. An include file. See comments on the top of the file.
  • stunts.as - like races.as but for stunt-missions. An include file. See comments on the top of the file.
  • mission_default.as - like terrain_default.as but for missions. Does the basic handling of .mission files.
  • road_utils.as - an include file, does parsing procedural roads using GenericDocument tokenizer.

New script API:

  • array<BeamClass@> game.getAllTrucks()
  • float game.getElapsedTime()
  • array<BeamClass@> BeamClass.getAllLinkedTrucks()
  • int BeamClass.getInstanceId()
  • Callback eventCallbackEx() - same as eventCallback() but with extra parameters.
  • event types SE_EVENTBOX_ENTER and SE_EVENTBOX_EXIT
  • terrain.addSurveyMapEntity(), terrain.delSurveyMapEntities()

Example:
Download the attached file and place it to your 'mods' directory. Note GitHub doesn't allow '.missionzip' extension, so I just used '.zip' - it works the same.
simple2circuit.zip
To test, load the "Simple Test Terrain" (simple2.terrn2), open in-game console and say 'loadmission simple2circuit.mission'.
screenshot_2023-02-12_03-43-43_1

@ohlidalp ohlidalp marked this pull request as draft February 12, 2023 03:22
@ohlidalp
Copy link
Member Author

IMPORTANT: Regenerate your cache! There was a bug and the mission entries are not filled properly.

I added rudimentary UI for missions:
obrazek
obrazek

@ohlidalp
Copy link
Member Author

@CuriousMike56 suggested creating multiplayer challenges using this system: https://forum.rigsofrods.org/threads/mission-system-prototype.3846/#post-19130
To help with the coding, I added rorserver scripting documentation to https://developer.rigsofrods.org/
obrazek

@ohlidalp
Copy link
Member Author

ohlidalp commented Feb 16, 2023

New script function: BeamClass.getAllLinkedTrucks()
obrazek

@ohlidalp
Copy link
Member Author

ohlidalp commented Feb 27, 2023

I added a new mission type: 'stunt'. It uses 2 eventboxes and measures various parameters while travelling between them.

This is a demo mission 'Simple Test Ramp' - the start eventbox is at the ramp, the finish eventbox is on the landing ground.
simple2ramp.zip
obrazek

@ohlidalp
Copy link
Member Author

ohlidalp commented Mar 25, 2023

TODO:

@TheJesser
Copy link
Collaborator

hey, It's theJesser from discord :) . We definitely need the ability to add icons onto the overview map for missions as suggested by mike on discord https://discord.com/channels/136544456244461568/189904947649708032/1089317783164166266

@ohlidalp
Copy link
Member Author

ohlidalp commented Mar 27, 2023

Map icons are yours:
obrazek

const int SURVEYMAP_ICONS_GROUPID = -222;

    TerrainClass@ terrn = game.getTerrain();
    if (ImGui::Button("Add icons"))
    {
        vector3 pos = game.getPersonPosition();
        terrn.addSurveyMapEntity("info!", "arrow_up.png", /*resource_group:*/"", /*caption:*/"", pos + vector3(0.f, 0.f, -100.f), /*angle:*/0.f, SURVEYMAP_ICONS_GROUPID);
        terrn.addSurveyMapEntity("warn!", "error.png", /*resource_group:*/"", /*caption:*/"", pos + vector3(-100.f, 0.f, 0.f), /*angle:*/-3.14/2, SURVEYMAP_ICONS_GROUPID);
        terrn.addSurveyMapEntity("error!", "exclamation.png", /*resource_group:*/"", /*caption:*/"", pos + vector3(100.f, 0.f, 0.f), /*angle:*/3.14/2, SURVEYMAP_ICONS_GROUPID);
    }

Info:
https://forum.rigsofrods.org/threads/mission-system-prototype.3846/

Features:
* A new mod type is recognized: *.mission - can add terrain objects, procedural roads and races to existing terrain.
* A new archive type is recognized: *.missionzip
* Mission types: only "race", more to come.
* To load a mission, open in-game console and say 'loadmission <filename>'
* Unloading missions is not implemented yet, use "top menubar / simulation / reload current terrain" to remove mission.

New scripts:
* missions.as - like races.as but for missions. An include file. See comments on the top of the file.
* mission_default.as - like terrain_default.as but for missions. Does the basic handling of .mission files.
* road_utils.as - an include file, does parsing procedural roads using GenericDocument tokenizer.

Code changes:
* Application.h - new LoaderType value 'LT_Mission'
* CacheSystem - added new mod+archive types, added mission details to CacheEntry
* GameContext - new function LoadMission()
* ConsoleCmd - new object LoadmissionCmd
* ScriptEngine - new category MISSION, new callbacks `un/loadMission()`, extended `addFunction()` to work with non-terrain scripts, new constant DEFAULT_MISSION_SCRIPT, updated constant DEFAULT_TERRAIN_SCRIPT
* GameScript - extended `spawnObject()` to work with non-terrain scripts.
FIXUP: ModCache::FillMissionDetailInfo() - use NAKED_STRINGS flag.
FIXUP: Improved error message of `ScriptEngine::invokeLoadMission()`
Codechanges:
* TopMenubar - new tab 'Missions', new button "Load a mission" in 'Simulation' tab.
* Application - new messages MSG_SIM_UN/LOAD_MISSION_REQUESTED
* main.cpp - processing new messages
* GameContext - changed LoadMission() to use CacheEntry*
* ConsoleCmd - call LoadMission() with CacheEntry* rather than filename.
* Terrain.cpp - dispose() - unload mission scripts
* ScriptEngine - added `missionEntry` to ScriptUnit, implemented `invokeUnloadMission()`
* ModCache - fixed bug in previous commit - misison details not filled.
* missions.as - implemented unloading.
* mission_default.as - actually try to unload.
FIXUP mission unloading - procedural roads not deleting.
The problem was removing collision boxes+tris from collision system was never implemented - the elements were only disabled and hiden, not removed.

Changes:
* New scripting API: `game.pruneCollisionElements()` - deletes boxes+tris marked for deletion.
* missions.as: invoke `game.pruneCollisionElements()` after unloading mission.
* CollisionsDebugUI: added button "prune collision elements" which forces the pruning.
* ProceduralRoad: fixed missing cleanup of collision mesh.
* Utils.h: `std::erase_if()` hack for Microsoft Visual Studio.
FIXUP: Collisions Debug UI: fixed typo in "Num collision meshes"
FIXUP: Portable `erase_if<>()`
The mission-specific callbacks `loadMission(filename, rg)` and `unloadMission()` were decomissioned; instead the name and RG of the .mission file gets delivered via SE_ANGELSCRIPT_MANIPULATIONS+ASMANIP_SCRIPT_LOADED, (params #6 and #7) and unloading is triggered by SE_ANGELSCRIPT_MANIPULATIONS+ASMANIP_SCRIPT_UNLOADING.

This archives the same result with less copypasted callback-invocation code.

Minor changes: MANIP_ enum fields got renamed to ASMANIP_ to match C++ with AngelScript. Doxygen docs were added.
I've set out to thoroughly study the race system script by Neorej16. I would like to create an editor for it and make it multiplayer. Then I'd like to extend it with other game modes.

First thing I noticed is that there's an extensible callback mechanism - modder can call `races.setCallback()` with callback type and function satisfying `funcdef void RACE_EVENT_CALLBACK(dictionary@);`. When invoked, the function gets all params as dictionary. The available callback types are nicely documented directly in the raceManager setup:
```
		// we initialize the callbacks dictionary
		this.callbacks.set("RaceFinish", null); // when a race was finished
		this.callbacks.set("RaceCancel", null); // when a race was canceled by any means (race_abort box or forbidden user action)
		this.callbacks.set("RaceStart",  null); // when a race starts
		this.callbacks.set("AdvanceLap", null); // when a lap is done, but not when the race is done
		this.callbacks.set("Checkpoint", null); // when a checkpoint is taken (excluding start and finish)
		this.callbacks.set("NewBestLap", null); // When a new best lap time is set
		this.callbacks.set("NewBestRace", null);// When a new best race time is set
		this.callbacks.set("LockedRace", null); // When the user passes the start line of a locked race
		this.callbacks.set("RaceEvent", null); // When the user passes the start line of a locked race
		this.callbacks.set("PenaltyEvent", null); // When the user gets in a race_penalty box, handled by the raceEvent method
		this.callbacks.set("AbortEvent", null); // When the user gets in a race_abort box, handled by the raceEvent method
```

As you can see, there can be only one common callback of every type, and the entry in `this.callbacks` is "type name => function pointer". That's enough though, as each checkpoint has instance name of scheme "checkpoint|$raceID|$sequenceNumber|$splitTrailNumber" (yes, the race system supports tracks with multi-checkpoint split sections!) and this went as parameter to the callback every time.

However, there was one callback type other than the rest - cancel point callback. The entry in `this.callback` was "instance name => dictionary", where dictionary held all the info normally embedded in the instance name, and also field named "callback" with the actual function pointer. Apparently the programmer intended to support multiple cancel points, each with it's own callback. I don't understand why, as following the checkpoint mechanic would archieve the same, only instead of 2 functions you'd have one that decides what to do based on the arguments dictionary. Either way, there was a bug in the code: the `cancelPointCount` was never updated, so there could effectively be just one common callback, and it was still broken because as I already wrote it didn't encode info to instance name but kept it inside the `this.callbacks` dictionary.

While deciding how to fix it, I noticed there are in total 3 (!) callbacks for race cancelling - the broken cancel points, "AbortEvent" and "RaceCancel". Apparently "RaceCancel" was invoked each time race was aborted for any reason. "AbortEvent", though, would only be invoked when driving through an event box called "race_abort". I fulltext-searched our resources if we have any .ODEF with such eventbox, and no we don't. So either map creators knew, or it went unused. Either way, "AbortEvent" was basically the same thing as the broken cancel points, except that it kept data in instance names like checkpoints do. So I ended up fixing the cancel points by actually redirecting them to "AbortEvent", only ignoring the box name.

I'd hate to break Neorej16's code so I tested using modified Auriga Proving Grounds terrain (which uses one cancel point). I added a secondary cancel point and tested they're indeed broken as I thought. Then I tested again with my fix.
@ohlidalp
Copy link
Member Author

Closing this as stale and mostly obsolete:

  • The scripting extensions were meanwhile merged in other PRs.
  • The server documentation is also already online: https://developer.rigsofrods.org/d2/d42/group___script_side_a_p_is.html
  • The missions implementation needs to be refined, in this order:
    1. working terrain editor;
    2. working terrain editor with race editing;
    3. working terrain editor with race+mission editing (the race system is pretty extensible already)

@ohlidalp ohlidalp closed this May 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants