Autoautosave
Table of Contents
- 1. About
- 2. License
- 3. Changelog
- 4. Options
- 5. Events
- 6. Cvars
- 7. Keys
- 8. Strings
- 9. Sounds
- 10. Source
- 11. Project setup
- 12. Tests
{
"old_version": "2.0.0",
"depends": {
"Fonts": "aas_",
"StringUtils": "aas_"
}
}
1. About
Universal autosaver.
It works as a configurable checkpoint system. It's designed to be compatible with any monster and weapon mod, and any map.
Features:
- autosave on various events: enemies alerted and killed, health or armor drops or rises, items are found, etc.
- periodic autosave.
- autosave on every event is optional.
- some events have fine-tuning options.
- optional screenshot taking when events happen.
- three options for event reporting: console, on-screen, and voice.
Autoautosave is a part of DoomToolbox.
2. License
SPDX-FileCopyrightText: © 2018 Alexander Kromm (m8f) <mmaulwurff@gmail.com> SPDX-License-Identifier: GPL-3.0-only
3. Changelog
v2.0.0
- cleaned up Cvar and event names
- removed voice types
- autosave tokens now follow items
- added silent autosave tokens
v1.6.3
- 2025-03-08 fix compatibility with GZDoom 4.14.1
- 2022-08-13 A few
language.txtchanges - 2022-08-13 Localize
aas_event.zsstrings
v1.6.2
- 2022-08-10 fix VM abort with Corruption Cards
v1.6.1
- 2022-08-08 fix floating point truncation
- 2022-08-06 fixed Brazilian Portuguese language code
v1.6.0
- 2022-08-05 add missing language string
- 2022-08-05 precache new voices
- 2022-08-05 inline queue class
- 2022-08-05 add voice volume slider
- 2022-08-04 add menu icon
- 2022-07-30 add Brazilian Portuguese localization
v1.5.0
- 2022-07-29 add minor cvar performance optimization
- 2022-07-29 replace hardcoded timeout with real voice length
- 2022-07-29 add new voice packs
- 2022-07-29 fix: Time passed event fires when player is dead
- 2022-07-29 optimize titlemap check
- 2022-07-25 clean up code
- 2020-08-06 fix doc for
aas_logger
v1.4.2
- 2020-08-06 add an option for log message color
- 2020-08-06 fix percent kill showing on quiet log setting
- 2020-08-05 unify
aas_loggerandaas_printer - 2020-08-05 don't emit active enemies events if player is dead
v1.4.1
- 2020-07-15 wrap
TICRATEwith a more meaningful name - 2020-07-12 don't trigger Armor Low event on death
- 2020-07-12 don't trigger Health Low event on death
v1.4
- 2020-07-12 add some descriptions
- 2020-07-11 add support for custom tokens
- 2020-07-11 rearrange options menu a bit
- 2020-07-11 add commands to disable/enable all voice and screenshots
- 2020-07-11 remove voice level option, move voice options to top menu
- 2020-07-11 remove never used pomodoro event
- 2020-07-11 add submenu for voice toggles
- 2020-07-11 add saving when % of enemies is killed
- 2020-07-11 shorten down kill notifications
- 2020-07-10 make major healing value configurable
- 2020-07-09 fix saving on items that fly in the air
- 2020-07-09 initialize counters with actual values instead of zeroes
- 2020-07-09 update Readme
- 2020-07-09 put handlers to a directory
- 2020-07-09 tick objects explicitly instead of them being Thinkers: avoid object duplication
- 2020-07-06 don't count items in hubs as dropped
- 2020-07-06 move clock classes to a separate directory
- 2020-07-04 disable autoautosave on titlemaps
- 2020-07-04 make log and voice options user-scoped
- 2020-07-04 fix console log level cvar default
- 2020-07-04 fix: Unknown event type
- 2020-07-04 fix emitting Time passed event after picking up stuff
- 2020-07-04 add two new voices
- 2020-07-04 precache sounds
- 2020-07-04 remove random pitch shift on voice sounds
- 2020-07-04 apply active enemies check logic to bosses too
- 2020-07-04 fix multiple new active enemies event emission
- 2020-07-03 move active enemies checker into a separate class
- 2020-07-03 update espeak for better quality sounds
- 2020-07-01 minor refactoring for some checks
- 2020-07-01 fixed some event descriptions
- 2020-07-01 refactor enemy-related events
- 2020-06-30 make core class a thinker so it can be profiled for performance
- 2020-06-29 make sound queue
- 2020-06-29 make event descriptions shorter
- 2020-06-27 update Readme
- 2020-06-27 clean up build scripts
- 2020-06-26 fix periodic save triggering after each autosave
- 2020-06-25 move strings from menudef to language, menu cleanup
- 2020-06-25 output all strings only through
aas_logfunctions - 2020-06-25 move strings from keyconf to language lump
- 2020-06-24 minor refactoring
- 2020-06-24 don't trigger save timer on zero time
- 2020-06-24 wrap one more cvar access
- 2020-06-24 separate saver and save timer
- 2020-06-23 make time dependency explicit
- 2020-06-23 wrap more cvar access
- 2020-06-23 wrap cvar access in event source
- 2020-06-22 make all checks every tick
- 2020-06-22 inject
game_actionsinterface into saver and screenshooter - 2020-06-22 make screenshot through a dedicated class
- 2020-06-21 code style
- 2020-06-21 replace virtual
aas_event_handler.init()with constructors - 2020-06-21 minor refactoring
- 2020-06-21 make a constructor for
aas_event_source - 2020-06-21 separate
EventHandlerandaas_event_source - 2020-06-21 more code formatting
- 2020-06-21 get rid of
OnRegister - 2020-06-21 fix sound files changing on each build
- 2020-06-20 make different implementation of manual autosave
- 2020-06-20 more code formatting
- 2020-06-19 reformat code
- 2020-06-19 add copyright notice to source code files
- 2020-06-19 replace deprecated function
- 2020-06-19 rename more classes
- 2020-06-19 rename sound files
- 2020-06-19 rename some classes
- 2020-06-19 disable compression
- 2020-06-19 move scripts to a directory
- 2020-06-19 bump required engine version
- 2020-06-19 rename files
- 2020-05-05 update Readme
- 2020-05-05 menudef cleanup
- 2020-05-05 add a note on Hideous Destructor keys
- 2020-05-05 make items not derived from Inventory spawn autosave tokens too
- 2020-05-05 cleanup build script and gitignore
- 2019-10-28 update Readme
v1.3
- 2019-10-28 fix autosave on things that replace keys
- 2019-10-25 add gitattributes
v1.2.2
- 2019-10-25 remove unnecessary spaces in menus
- 2019-10-25 fix: not being able to exit if autosave is in progress
- 2019-04-11 minor readability changes
- 2019-03-24 bump up minimal required ZScript version
v1.2.1
- 2019-03-24 add armor pickup event
- 2019-03-24 rename ZScript files to .zs
- 2019-03-24 add a toggle for voice on teleport
- 2019-03-20 add global screenshots toggle to screenshot options menu
v1.2
- 2019-03-03 use of experimental game action access
v1.1
- 2019-02-18 added an option to make a screenshot on last health percent
- 2019-02-18 fixed repeating "Time Passed" messages
- 2019-01-19 add an option to make a screenshot on level start
v1.0.2
- 2018-12-30 fix save token spawning in not exact places
v1.0.1
- 2018-12-23 fix not being able to take a screen shot and save on a single event
v1.0
- 2018-12-08 change to GPLv3 license, update Readme
- 2018-12-08 introduce code structure
- 2018-12-08 add saving on QCDE keys
- 2018-11-18 fix not saving on Final Doomer keys
v0.12
- 2018-09-29 add optional screen shots on events
v0.11
- 2018-07-28 update readme
- 2018-07-28 add Rebel Rumble powerups
- 2018-07-27 update README
v0.10.5
- 2018-07-27 add null checks in
WorldThingSpawnedevent
v0.10.4
- 2018-07-21 fix sounds AGAIN
v0.10.3
- 2018-07-02 fix voice, add forgotten options, add autosave on GS Big Coins
- 2018-07-01 make changelog pretty
v0.10.2
- 2018-07-01 remove saving right after loading from game saved by manual save
v0.10.1
- 2018-07-01 fix manual autosave, update README
- 2018-07-01 remove debug output
v0.10
- 2018-06-24 fixed: secret found event was triggered after loading
- 2018-06-24 fix bug with logging time passing when periodic saving is off
v0.9.5
- 2018-06-24 minor fixes
- 2018-06-24 add sndinfo and sound generation to gen-files.sh
- 2018-06-22 add event types table, generate files from it
- 2018-06-21 add interface for pomodoro, fix manual save
- 2018-06-20 code refactoring, better structure
- 2018-06-17 comment out debug output
v0.9.4
- 2018-06-17 minor fixes
- 2018-06-17 add forgotten "Save on dropped items" menu toggle
v0.9.3
- 2018-06-17 fix crash with NC HUD and alternative GZDoom HUD
- 2018-06-17 remove leftovers
v0.9.2
- 2018-06-17 remove remaining ACS code (thanks to Zhs2)
- 2018-06-17 update sounds?
- 2018-06-17 fix Trailblazer backpack
v0.9.1
- 2018-06-16 format sounds to ogg
- 2018-06-16 add sounds to repository
- 2018-06-16 update README
- 2018-06-16 add changelog to archive
- 2018-06-16 tune default cvar value
v0.9
- 2018-06-16 restore keyconf
- 2018-06-16 added menudef, changed default cvar values
- 2018-06-16 fix messages
- 2018-06-16 fix
min_save_wait - 2018-06-16 add voice, add cvars
- 2018-06-16 add big healing event
- 2018-06-16 add new armor event
- 2018-06-15 add armor rise and drop events
- 2018-06-14 add two health events
- 2018-06-14 rewrite in progress
- 2018-06-09 add mods to build script
- 2018-05-13 add logging option
v0.8.1
- 2018-04-10 add Adventures of Square Goonades as weapon
- 2018-04-10 set default min seconds to 10
v0.8
- 2018-04-02 update README
- 2018-04-02 add autosave on teleport (default off)
- 2018-03-29 add trailblazer powerups support
v0.7
- 2018-03-28 add option to save on backpack and save or not on dropped things
v0.5
- 2018-03-25 fix enabled/disabled autoautosave
- 2018-03-25 improve and fix health/armor drop/rise settings
- 2018-03-24 make autosave on keys, weapons, and powerups instantaneous
v0.4
- 2018-03-24 fix save on boss kill (all monsters were considered bosses)
v0.3
- 2018-03-24 add save on alert
- 2018-03-23 add key options for quicksave and quickload
- 2018-03-22 add option to disable save on health/armor change
v0.2
- 2018-03-22 increase max health and armor in options
- 2018-03-22 update README
- 2018-03-22 autosave even if things are spawned directly in inventory
- 2018-03-22 more options
- 2018-03-22 add toggle
- 2018-03-22 more options
- 2018-03-22 implement options
- 2018-03-22 implement autosave on reaching health/armor level
- 2018-03-22 add autosave on key pickup and manual autosave key
- 2018-03-22 make universal save on boss kill
- 2018-03-21 implement autosave on key pickup
- 2018-03-21 implement autosave on BFG pickup
- 2018-03-21 implement autosave health limit
- 2018-03-21 implement autosave on killing cyber and spider
- 2018-03-21 implement autosave with period
- 2018-03-21 implement autosave on radsuit pickup
- 2018-03-21 implement autosave on heal > 25
- 2018-03-21 implement autosave on armor type change
- 2018-03-21 don't autosave if 30 seconds didn't pass
- 2018-03-21 implement autosave on secret found
- 2018-03-21 implement autosave on all monsters killed and all items found
4. Options
AddOptionMenu OptionsMenu { Submenu "$AAS_OPTIONS_TITLE", aas_Options } OptionValue aas_log_level_options { -1, "$AAS_LEVEL_NOTHING" 13, "$AAS_LEVEL_QUIET" 199, "$AAS_LEVEL_VERBOSE" 999, "$AAS_LEVEL_EVERYTHING" } OptionMenu aas_Options { Title "$AAS_OPTIONS_TITLE" StaticText "" StaticText "$AAS_MUST_ALWAYS" , Black StaticText "$OPTVAL_ALWAYS" , Black Option "$MISCMNU_EnableAutosaves" , disableAutosave, Autosave Slider "$MISCMNU_AutosaveCount" , autosaveCount, 1, 20, 1, 0 StaticText "" StaticText "$AAS_GLOBAL_TOGGLE" , 1 Option "$AAS_TITLE" , aas_enabled, OnOff StaticText "" StaticText "$AAS_CONTROLS" , 1 Control "$AAS_MANUAL_CONTROL_NAME" , aas_manual_autosave Control "$AAS_TOGGLE_CONTROL_NAME" , aas_toggle StaticText "" Submenu "$AAS_EVENT_TUNING_TITLE" , aas_EventTuningMenu Submenu "$AAS_REPORT_OPTIONS_TITLE" , aas_LogOptions StaticText "" Submenu "$AAS_INDIVIDUAL_TOGGLES_TITLE" , aas_EventTogglesMenu Submenu "$AAS_SCREENSHOT_TOGGLES_TITLE" , aas_ScreenshotOptions Submenu "$AAS_VOICE_OPTIONS_TITLE" , aas_VoiceOptions } OptionMenu aas_ScreenshotOptions { Title "$AAS_SCREENSHOT_TOGGLES_TITLE" StaticText "$AAS_SCREENSHOTS_WARNING" , Black StaticText "" Option "$MISCMNU_EnableScriptScreenshots" , enableScriptScreenshot , OnOff StaticText "$AAS_SCREENSHOTS_SCRIPT_NOTE" , Black StaticText "" SafeCommand "$AAS_ENABLE_ALL" , aas_set_screenshots_on SafeCommand "$AAS_DISABLE_ALL" , aas_set_screenshots_off StaticText "" Option "$AAS_EVENT_MANUAL" , aas_screenshot_on_manual , OnOff Option "$AAS_EVENT_LEVEL_START" , aas_screenshot_on_level_start , OnOff StaticText "" Option "$AAS_EVENT_BOSS_ALERT" , aas_screenshot_on_boss_alert , OnOff Option "$AAS_EVENT_BOSS_KILL" , aas_screenshot_on_boss_kill , OnOff Option "$AAS_EVENT_GROUP_ALERT" , aas_screenshot_on_group_alert , OnOff Option "$AAS_EVENT_GROUP_KILL" , aas_screenshot_on_group_kill , OnOff Option "$AAS_EVENT_ALL_KILL" , aas_screenshot_on_all_kill , OnOff StaticText "" Option "$AAS_EVENT_ALL_ITEMS_FOUND", aas_screenshot_on_all_items_found , OnOff Option "$AAS_EVENT_TIME_PERIOD" , aas_screenshot_on_time_period , OnOff Option "$AAS_EVENT_TELEPORT" , aas_screenshot_on_teleport , OnOff Option "$AAS_EVENT_SECRET_FOUND" , aas_screenshot_on_secret_found , OnOff StaticText "" Option "$AAS_EVENT_HEALTH_DROP" , aas_screenshot_on_health_drop , OnOff Option "$AAS_EVENT_ONE_PERCENT" , aas_screenshot_on_one_percent , OnOff Option "$AAS_EVENT_HEALTH_RISE" , aas_screenshot_on_health_rise , OnOff Option "$AAS_EVENT_BIG_HEAL" , aas_screenshot_on_big_heal , OnOff StaticText "" Option "$AAS_EVENT_NEW_ARMOR" , aas_screenshot_on_new_armor , OnOff Option "$AAS_EVENT_ARMOR" , aas_screenshot_on_armor , OnOff Option "$AAS_EVENT_ARMOR_DROP" , aas_screenshot_on_armor_drop , OnOff Option "$AAS_EVENT_ARMOR_RISE" , aas_screenshot_on_armor_rise , OnOff StaticText "" Option "$AAS_EVENT_POWERUP" , aas_screenshot_on_powerup , OnOff Option "$AAS_EVENT_WEAPON" , aas_screenshot_on_weapon , OnOff Option "$AAS_EVENT_KEY" , aas_screenshot_on_key , OnOff Option "$AAS_EVENT_BACKPACK" , aas_screenshot_on_backpack , OnOff Option "$AAS_EVENT_IMPORTANT" , aas_screenshot_on_important , OnOff StaticText "" Option "$AAS_EVENT_ENEMIES_10" , aas_screenshot_on_enemies_10 , OnOff Option "$AAS_EVENT_ENEMIES_20" , aas_screenshot_on_enemies_20 , OnOff Option "$AAS_EVENT_ENEMIES_30" , aas_screenshot_on_enemies_30 , OnOff Option "$AAS_EVENT_ENEMIES_40" , aas_screenshot_on_enemies_40 , OnOff Option "$AAS_EVENT_ENEMIES_50" , aas_screenshot_on_enemies_50 , OnOff Option "$AAS_EVENT_ENEMIES_60" , aas_screenshot_on_enemies_60 , OnOff Option "$AAS_EVENT_ENEMIES_70" , aas_screenshot_on_enemies_70 , OnOff Option "$AAS_EVENT_ENEMIES_80" , aas_screenshot_on_enemies_80 , OnOff Option "$AAS_EVENT_ENEMIES_90" , aas_screenshot_on_enemies_90 , OnOff } OptionMenu aas_LogOptions { Title "$AAS_REPORT_OPTIONS_TITLE" StaticText "" Option "$AAS_LOG_CONSOLE" , aas_console_log_level , aas_log_level_options Option "$AAS_LOG_SCREEN" , aas_screen_level , aas_log_level_options StaticText "" StaticText "$AAS_LOG_QUIET_NOTE_1" , Black StaticText "$AAS_LOG_QUIET_NOTE_2" , Black StaticText "" StaticText "$AAS_LOG_VERBOSE_NOTE_1" , Black StaticText "$AAS_LOG_VERBOSE_NOTE_2" , Black StaticText "" Option "$AAS_PRINT_COLOR" , aas_print_color , TextColors } OptionMenu aas_VoiceOptions { Title "$AAS_VOICE_OPTIONS_TITLE" Slider "$AAS_VOICE_VOLUME" , aas_voice_volume, 1, 10, 1, 0 StaticText "" SafeCommand "$AAS_ENABLE_ALL" , aas_set_voice_on SafeCommand "$AAS_DISABLE_ALL" , aas_set_voice_off StaticText "" Option "$AAS_EVENT_MANUAL" , aas_voice_on_manual , OnOff Option "$AAS_EVENT_LEVEL_START" , aas_voice_on_level_start , OnOff StaticText "" Option "$AAS_EVENT_BOSS_ALERT" , aas_voice_on_boss_alert , OnOff Option "$AAS_EVENT_BOSS_KILL" , aas_voice_on_boss_kill , OnOff Option "$AAS_EVENT_GROUP_ALERT" , aas_voice_on_group_alert , OnOff Option "$AAS_EVENT_GROUP_KILL" , aas_voice_on_group_kill , OnOff Option "$AAS_EVENT_ALL_KILL" , aas_voice_on_all_kill , OnOff StaticText "" Option "$AAS_EVENT_ALL_ITEMS_FOUND", aas_voice_on_all_items_found , OnOff Option "$AAS_EVENT_TIME_PERIOD" , aas_voice_on_time_period , OnOff Option "$AAS_EVENT_TELEPORT" , aas_voice_on_teleport , OnOff Option "$AAS_EVENT_SECRET_FOUND" , aas_voice_on_secret_found , OnOff StaticText "" Option "$AAS_EVENT_HEALTH_DROP" , aas_voice_on_health_drop , OnOff Option "$AAS_EVENT_ONE_PERCENT" , aas_voice_on_one_percent , OnOff Option "$AAS_EVENT_HEALTH_RISE" , aas_voice_on_health_rise , OnOff Option "$AAS_EVENT_BIG_HEAL" , aas_voice_on_big_heal , OnOff StaticText "" Option "$AAS_EVENT_NEW_ARMOR" , aas_voice_on_new_armor , OnOff Option "$AAS_EVENT_ARMOR" , aas_voice_on_armor , OnOff Option "$AAS_EVENT_ARMOR_DROP" , aas_voice_on_armor_drop , OnOff Option "$AAS_EVENT_ARMOR_RISE" , aas_voice_on_armor_rise , OnOff StaticText "" Option "$AAS_EVENT_POWERUP" , aas_voice_on_powerup , OnOff Option "$AAS_EVENT_WEAPON" , aas_voice_on_weapon , OnOff Option "$AAS_EVENT_KEY" , aas_voice_on_key , OnOff Option "$AAS_EVENT_BACKPACK" , aas_voice_on_backpack , OnOff Option "$AAS_EVENT_IMPORTANT" , aas_voice_on_important , OnOff StaticText "" Option "$AAS_EVENT_ENEMIES_10" , aas_voice_on_enemies_10 , OnOff Option "$AAS_EVENT_ENEMIES_20" , aas_voice_on_enemies_20 , OnOff Option "$AAS_EVENT_ENEMIES_30" , aas_voice_on_enemies_30 , OnOff Option "$AAS_EVENT_ENEMIES_40" , aas_voice_on_enemies_40 , OnOff Option "$AAS_EVENT_ENEMIES_50" , aas_voice_on_enemies_50 , OnOff Option "$AAS_EVENT_ENEMIES_60" , aas_voice_on_enemies_60 , OnOff Option "$AAS_EVENT_ENEMIES_70" , aas_voice_on_enemies_70 , OnOff Option "$AAS_EVENT_ENEMIES_80" , aas_voice_on_enemies_80 , OnOff Option "$AAS_EVENT_ENEMIES_90" , aas_voice_on_enemies_90 , OnOff } OptionMenu aas_EventTuningMenu { Title "$AAS_EVENT_TUNING_TITLE" StaticText "" Slider "$AAS_MIN_BOSS_HEALTH" , aas_min_boss_health , 0, 20000, 200, 0 Slider "$AAS_GROUP_NUMBER" , aas_group_number , 1, 50, 1, 0 Slider "$AAS_BOSS_GROUP_NUMBER" , aas_boss_group_number, 1, 50, 1, 0 StaticText "" Slider "$AAS_HEALTH_LIMIT" , aas_health_limit , 0, 500, 5, 0 Slider "$AAS_BIG_HEAL_SIZE" , aas_big_heal , 5, 200, 5, 0 StaticText "" TextField "$AAS_LOW_HEALTH_THRESHOLD" , aas_health_threshold_down TextField "$AAS_HIGH_HEALTH_THRESHOLD" , aas_health_threshold_up TextField "$AAS_LOW_ARMOR_THRESHOLD" , aas_armor_threshold_down TextField "$AAS_HIGH_ARMOR_THRESHOLD" , aas_armor_threshold_up StaticText "" Slider "$AAS_MIN_SAVE_WAIT" , aas_min_save_wait , 0, 120, 5, 0 Slider "$AAS_PERIOD" , aas_autosave_period , 30, 600, 30, 0 StaticText "" Option "$AAS_SAVE_ON_DROPPED" , aas_save_on_dropped , OnOff StaticText "$AAS_SAVE_ON_DROPPED_NOTE" , Black } OptionMenu aas_EventTogglesMenu { Title "$AAS_INDIVIDUAL_TOGGLES_TITLE" Option "$AAS_EVENT_BOSS_ALERT" , aas_save_on_boss_alert , OnOff Option "$AAS_EVENT_BOSS_KILL" , aas_save_on_boss_kill , OnOff Option "$AAS_EVENT_GROUP_ALERT" , aas_save_on_group_alert , OnOff Option "$AAS_EVENT_GROUP_KILL" , aas_save_on_group_kill , OnOff Option "$AAS_EVENT_ALL_KILL" , aas_save_on_all_kill , OnOff StaticText "" Option "$AAS_EVENT_ALL_ITEMS_FOUND", aas_save_on_all_items_found , OnOff Option "$AAS_EVENT_TIME_PERIOD" , aas_save_on_time_period , OnOff Option "$AAS_EVENT_TELEPORT" , aas_save_on_teleport , OnOff Option "$AAS_EVENT_SECRET_FOUND" , aas_save_on_secret_found , OnOff StaticText "" Option "$AAS_EVENT_HEALTH_DROP" , aas_save_on_health_drop , OnOff Option "$AAS_EVENT_HEALTH_RISE" , aas_save_on_health_rise , OnOff Option "$AAS_EVENT_BIG_HEAL" , aas_save_on_big_heal , OnOff StaticText "" Option "$AAS_EVENT_NEW_ARMOR" , aas_save_on_new_armor , OnOff Option "$AAS_EVENT_ARMOR" , aas_save_on_armor , OnOff Option "$AAS_EVENT_ARMOR_DROP" , aas_save_on_armor_drop , OnOff Option "$AAS_EVENT_ARMOR_RISE" , aas_save_on_armor_rise , OnOff StaticText "" Option "$AAS_EVENT_POWERUP" , aas_save_on_powerup , OnOff Option "$AAS_EVENT_WEAPON" , aas_save_on_weapon , OnOff Option "$AAS_EVENT_KEY" , aas_save_on_key , OnOff Option "$AAS_EVENT_BACKPACK" , aas_save_on_backpack , OnOff Option "$AAS_EVENT_IMPORTANT" , aas_save_on_important , OnOff StaticText "" Option "$AAS_EVENT_ENEMIES_10" , aas_save_on_enemies_10 , OnOff Option "$AAS_EVENT_ENEMIES_20" , aas_save_on_enemies_20 , OnOff Option "$AAS_EVENT_ENEMIES_30" , aas_save_on_enemies_30 , OnOff Option "$AAS_EVENT_ENEMIES_40" , aas_save_on_enemies_40 , OnOff Option "$AAS_EVENT_ENEMIES_50" , aas_save_on_enemies_50 , OnOff Option "$AAS_EVENT_ENEMIES_60" , aas_save_on_enemies_60 , OnOff Option "$AAS_EVENT_ENEMIES_70" , aas_save_on_enemies_70 , OnOff Option "$AAS_EVENT_ENEMIES_80" , aas_save_on_enemies_80 , OnOff Option "$AAS_EVENT_ENEMIES_90" , aas_save_on_enemies_90 , OnOff }
5. Events
| N | Enum | Default | Text |
|---|---|---|---|
| 0 | all_kill |
true | All enemies down |
| 1 | boss_kill |
true | Boss down |
| 2 | group_kill |
false | No active enemies |
| 3 | group_alert |
false | New active enemies |
| 4 | boss_alert |
true | Boss alerted |
| 5 | all_items_found |
true | All items found |
| 7 | time_period |
false | Time passed |
| 8 | teleport |
false | Moved to another place |
| 9 | manual |
true | Manual save |
| 11 | important |
true | Something important! |
| 12 | level_start |
false | Level started |
| 13 | one_percent |
false | Last health percent! |
| 14 | enemies_10 |
false | Enemies 10% down |
| 15 | enemies_20 |
false | Enemies 20% down |
| 16 | enemies_30 |
false | Enemies 30% down |
| 17 | enemies_40 |
false | Enemies 40% down |
| 18 | enemies_50 |
false | Enemies 50% down |
| 19 | enemies_60 |
false | Enemies 60% down |
| 20 | enemies_70 |
false | Enemies 70% down |
| 21 | enemies_80 |
false | Enemies 80% down |
| 22 | enemies_90 |
false | Enemies 90% down |
| 100 | health_drop |
false | Health low |
| 101 | health_rise |
false | Health high |
| 102 | armor_drop |
false | Armor low |
| 103 | armor_rise |
false | Armor high |
| 104 | secret_found |
true | Secret found |
| 200 | powerup |
true | Powerup found |
| 201 | weapon |
true | Weapon found |
| 202 | key |
true | Key found |
| 203 | backpack |
true | Backpack found |
| 204 | new_armor |
true | New armor |
| 205 | big_heal |
true | Major healing |
| 206 | armor |
false | Armor found |
6. Cvars
// Global toggle server bool aas_enabled = true; // Event tuning server int aas_min_boss_health = 2000; server int aas_health_limit = 25; server int aas_min_save_wait = 10; // seconds server int aas_autosave_period = 120; // seconds server int aas_group_number = 10; server int aas_boss_group_number = 1; server int aas_health_threshold_down = 50; server int aas_health_threshold_up = 100; server int aas_armor_threshold_down = 25; server int aas_armor_threshold_up = 100; server bool aas_save_on_dropped = false; server int aas_big_heal = 50; // Logs and voice // -1 - nothing; // 99 - quiet: enemies, time, teleport, items; // 199 - verbose: enemies, time, teleport, items, health, armor; // 999 - everything. user int aas_console_log_level = -1; user int aas_screen_level = -1; user int aas_voice_volume = 1; user int aas_print_color = 0; // Cvars that set if saving or not server bool aas_save_on_all_kill = true; server bool aas_save_on_boss_kill = true; server bool aas_save_on_group_kill = false; server bool aas_save_on_group_alert = false; server bool aas_save_on_boss_alert = true; server bool aas_save_on_all_items_found = true; server bool aas_save_on_time_period = false; server bool aas_save_on_teleport = false; server bool aas_save_on_manual = true; server bool aas_save_on_important = true; server bool aas_save_on_level_start = false; server bool aas_save_on_one_percent = false; server bool aas_save_on_enemies_10 = false; server bool aas_save_on_enemies_20 = false; server bool aas_save_on_enemies_30 = false; server bool aas_save_on_enemies_40 = false; server bool aas_save_on_enemies_50 = false; server bool aas_save_on_enemies_60 = false; server bool aas_save_on_enemies_70 = false; server bool aas_save_on_enemies_80 = false; server bool aas_save_on_enemies_90 = false; server bool aas_save_on_health_drop = false; server bool aas_save_on_health_rise = false; server bool aas_save_on_armor_drop = false; server bool aas_save_on_armor_rise = false; server bool aas_save_on_secret_found = true; server bool aas_save_on_powerup = true; server bool aas_save_on_weapon = true; server bool aas_save_on_key = true; server bool aas_save_on_backpack = true; server bool aas_save_on_new_armor = true; server bool aas_save_on_big_heal = true; server bool aas_save_on_armor = false; // Cvars that set if taking a screenshot or not server bool aas_screenshot_on_all_kill = false; server bool aas_screenshot_on_boss_kill = false; server bool aas_screenshot_on_group_kill = false; server bool aas_screenshot_on_group_alert = false; server bool aas_screenshot_on_boss_alert = false; server bool aas_screenshot_on_all_items_found = false; server bool aas_screenshot_on_time_period = false; server bool aas_screenshot_on_teleport = false; server bool aas_screenshot_on_manual = false; server bool aas_screenshot_on_important = false; server bool aas_screenshot_on_level_start = false; server bool aas_screenshot_on_one_percent = false; server bool aas_screenshot_on_enemies_10 = false; server bool aas_screenshot_on_enemies_20 = false; server bool aas_screenshot_on_enemies_30 = false; server bool aas_screenshot_on_enemies_40 = false; server bool aas_screenshot_on_enemies_50 = false; server bool aas_screenshot_on_enemies_60 = false; server bool aas_screenshot_on_enemies_70 = false; server bool aas_screenshot_on_enemies_80 = false; server bool aas_screenshot_on_enemies_90 = false; server bool aas_screenshot_on_health_drop = false; server bool aas_screenshot_on_health_rise = false; server bool aas_screenshot_on_armor_drop = false; server bool aas_screenshot_on_armor_rise = false; server bool aas_screenshot_on_secret_found = false; server bool aas_screenshot_on_powerup = false; server bool aas_screenshot_on_weapon = false; server bool aas_screenshot_on_key = false; server bool aas_screenshot_on_backpack = false; server bool aas_screenshot_on_new_armor = false; server bool aas_screenshot_on_big_heal = false; server bool aas_screenshot_on_armor = false; // Cvars that set if making a voice announcement or not server bool aas_voice_on_all_kill = true; server bool aas_voice_on_boss_kill = true; server bool aas_voice_on_group_kill = true; server bool aas_voice_on_group_alert = true; server bool aas_voice_on_boss_alert = true; server bool aas_voice_on_all_items_found = true; server bool aas_voice_on_time_period = true; server bool aas_voice_on_teleport = true; server bool aas_voice_on_manual = true; server bool aas_voice_on_important = true; server bool aas_voice_on_level_start = true; server bool aas_voice_on_one_percent = true; server bool aas_voice_on_enemies_10 = true; server bool aas_voice_on_enemies_20 = true; server bool aas_voice_on_enemies_30 = true; server bool aas_voice_on_enemies_40 = true; server bool aas_voice_on_enemies_50 = true; server bool aas_voice_on_enemies_60 = true; server bool aas_voice_on_enemies_70 = true; server bool aas_voice_on_enemies_80 = true; server bool aas_voice_on_enemies_90 = true; server bool aas_voice_on_health_drop = true; server bool aas_voice_on_health_rise = true; server bool aas_voice_on_armor_drop = true; server bool aas_voice_on_armor_rise = true; server bool aas_voice_on_secret_found = true; server bool aas_voice_on_powerup = true; server bool aas_voice_on_weapon = true; server bool aas_voice_on_key = true; server bool aas_voice_on_backpack = true; server bool aas_voice_on_new_armor = true; server bool aas_voice_on_big_heal = true; server bool aas_voice_on_armor = true;
7. Keys
// Aliases Alias aas_set_voice_on "aas_voice_on_all_kill 1; aas_voice_on_boss_kill 1; aas_voice_on_group_kill 1; aas_voice_on_group_alert 1; aas_voice_on_boss_alert 1; aas_voice_on_all_items_found 1; aas_voice_on_time_period 1; aas_voice_on_teleport 1; aas_voice_on_manual 1; aas_voice_on_important 1; aas_voice_on_level_start 1; aas_voice_on_one_percent 1; aas_voice_on_enemies_10 1; aas_voice_on_enemies_20 1; aas_voice_on_enemies_30 1; aas_voice_on_enemies_40 1; aas_voice_on_enemies_50 1; aas_voice_on_enemies_60 1; aas_voice_on_enemies_70 1; aas_voice_on_enemies_80 1; aas_voice_on_enemies_90 1; aas_voice_on_health_drop 1; aas_voice_on_health_rise 1; aas_voice_on_armor_drop 1; aas_voice_on_armor_rise 1; aas_voice_on_secret_found 1; aas_voice_on_powerup 1; aas_voice_on_weapon 1; aas_voice_on_key 1; aas_voice_on_backpack 1; aas_voice_on_new_armor 1; aas_voice_on_big_heal 1; aas_voice_on_armor 1" Alias aas_set_voice_off "aas_voice_on_all_kill 0; aas_voice_on_boss_kill 0; aas_voice_on_group_kill 0; aas_voice_on_group_alert 0; aas_voice_on_boss_alert 0; aas_voice_on_all_items_found 0; aas_voice_on_time_period 0; aas_voice_on_teleport 0; aas_voice_on_manual 0; aas_voice_on_important 0; aas_voice_on_level_start 0; aas_voice_on_one_percent 0; aas_voice_on_enemies_10 0; aas_voice_on_enemies_20 0; aas_voice_on_enemies_30 0; aas_voice_on_enemies_40 0; aas_voice_on_enemies_50 0; aas_voice_on_enemies_60 0; aas_voice_on_enemies_70 0; aas_voice_on_enemies_80 0; aas_voice_on_enemies_90 0; aas_voice_on_health_drop 0; aas_voice_on_health_rise 0; aas_voice_on_armor_drop 0; aas_voice_on_armor_rise 0; aas_voice_on_secret_found 0; aas_voice_on_powerup 0; aas_voice_on_weapon 0; aas_voice_on_key 0; aas_voice_on_backpack 0; aas_voice_on_new_armor 0; aas_voice_on_big_heal 0; aas_voice_on_armor 0" Alias aas_set_screenshots_on "aas_screenshot_on_all_kill 1; aas_screenshot_on_boss_kill 1; aas_screenshot_on_group_kill 1; aas_screenshot_on_group_alert 1; aas_screenshot_on_boss_alert 1; aas_screenshot_on_all_items_found 1; aas_screenshot_on_time_period 1; aas_screenshot_on_teleport 1; aas_screenshot_on_manual 1; aas_screenshot_on_important 1; aas_screenshot_on_level_start 1; aas_screenshot_on_one_percent 1; aas_screenshot_on_enemies_10 1; aas_screenshot_on_enemies_20 1; aas_screenshot_on_enemies_30 1; aas_screenshot_on_enemies_40 1; aas_screenshot_on_enemies_50 1; aas_screenshot_on_enemies_60 1; aas_screenshot_on_enemies_70 1; aas_screenshot_on_enemies_80 1; aas_screenshot_on_enemies_90 1; aas_screenshot_on_health_drop 1; aas_screenshot_on_health_rise 1; aas_screenshot_on_armor_drop 1; aas_screenshot_on_armor_rise 1; aas_screenshot_on_secret_found 1; aas_screenshot_on_powerup 1; aas_screenshot_on_weapon 1; aas_screenshot_on_key 1; aas_screenshot_on_backpack 1; aas_screenshot_on_new_armor 1; aas_screenshot_on_big_heal 1; aas_screenshot_on_armor 1" Alias aas_set_screenshots_off "aas_screenshot_on_all_kill 0; aas_screenshot_on_boss_kill 0; aas_screenshot_on_group_kill 0; aas_screenshot_on_group_alert 0; aas_screenshot_on_boss_alert 0; aas_screenshot_on_all_items_found 0; aas_screenshot_on_time_period 0; aas_screenshot_on_teleport 0; aas_screenshot_on_manual 0; aas_screenshot_on_important 0; aas_screenshot_on_level_start 0; aas_screenshot_on_one_percent 0; aas_screenshot_on_enemies_10 0; aas_screenshot_on_enemies_20 0; aas_screenshot_on_enemies_30 0; aas_screenshot_on_enemies_40 0; aas_screenshot_on_enemies_50 0; aas_screenshot_on_enemies_60 0; aas_screenshot_on_enemies_70 0; aas_screenshot_on_enemies_80 0; aas_screenshot_on_enemies_90 0; aas_screenshot_on_health_drop 0; aas_screenshot_on_health_rise 0; aas_screenshot_on_armor_drop 0; aas_screenshot_on_armor_rise 0; aas_screenshot_on_secret_found 0; aas_screenshot_on_powerup 0; aas_screenshot_on_weapon 0; aas_screenshot_on_key 0; aas_screenshot_on_backpack 0; aas_screenshot_on_new_armor 0; aas_screenshot_on_big_heal 0; aas_screenshot_on_armor 0" // Keys AddKeySection $AAS_TITLE AutoautosaveKeys Alias aas_manual_autosave "netevent aas_manual_save:9" AddMenuKey $AAS_MANUAL_CONTROL_NAME aas_manual_autosave Alias aas_on "aas_enabled 1; netevent aas_log:AAS_TOGGLE_ON_MESSAGE" Alias aas_off "aas_enabled 0; netevent aas_log:AAS_TOGGLE_OFF_MESSAGE" Alias aas_toggle "test $aas_enabled aas_off aas_on" AddMenuKey $AAS_TOGGLE_CONTROL_NAME aas_toggle
8. Strings
8.1. English (US)
[enu default] AAS_TITLE = "Autoautosave"; AAS_MANUAL_CONTROL_NAME = "Manual autosave"; AAS_TOGGLE_CONTROL_NAME = "Toggle Autoautosave"; AAS_TOGGLE_ON_MESSAGE = "Autoautosave enabled"; AAS_TOGGLE_OFF_MESSAGE = "Autoautosave disabled"; AAS_LEVEL_NOTHING = "nothing"; AAS_LEVEL_QUIET = "quiet"; AAS_LEVEL_VERBOSE = "verbose"; AAS_LEVEL_EVERYTHING = "everything"; AAS_OPTIONS_TITLE = "Autoautosave \ct⍗"; AAS_MUST_ALWAYS = "This must be set to"; AAS_GLOBAL_TOGGLE = "Global toggle"; AAS_CONTROLS = "Controls"; AAS_EVENT_TUNING_TITLE = "Event Options"; AAS_REPORT_OPTIONS_TITLE = "Log Options"; AAS_INDIVIDUAL_TOGGLES_TITLE = "Autosave Toggles"; AAS_SCREENSHOT_TOGGLES_TITLE = "Screenshot Toggles"; AAS_VOICE_OPTIONS_TITLE = "Voice Toggles"; AAS_SCREENSHOTS_WARNING = "Warning: too many screenshot can use up disk space."; AAS_SCREENSHOTS_SCRIPT_NOTE = "This must be set to \"On\"."; AAS_LOG_CONSOLE = "Log to console"; AAS_LOG_SCREEN = "Log to screen"; AAS_PRINT_COLOR = "Log color"; AAS_LOG_QUIET_NOTE_1 = "Quiet:"; AAS_LOG_QUIET_NOTE_2 = "enemies, time, teleport, items."; AAS_LOG_VERBOSE_NOTE_1 = "Verbose:"; AAS_LOG_VERBOSE_NOTE_2 = "enemies, time, teleport, items, % kills, health, armor."; AAS_MIN_BOSS_HEALTH = "Enemy is a boss when health is at least"; AAS_GROUP_NUMBER = "Group is at least N enemies"; AAS_BOSS_GROUP_NUMBER = "Boss group is at least N enemies"; AAS_HEALTH_LIMIT = "Don't save when health is less than"; AAS_BIG_HEAL_SIZE = "Major healing amount"; AAS_LOW_HEALTH_THRESHOLD = "Low health threshold"; AAS_HIGH_HEALTH_THRESHOLD = "High health threshold"; AAS_LOW_ARMOR_THRESHOLD = "Low armor threshold"; AAS_HIGH_ARMOR_THRESHOLD = "High armor threshold"; AAS_MIN_SAVE_WAIT = "Don't save until seconds passed"; AAS_PERIOD = "Autosave every N seconds"; AAS_SAVE_ON_DROPPED = "Save on dropped items"; AAS_SAVE_ON_DROPPED_NOTE = "Also picking up Hideous Destructor keys."; AAS_VOICE_VOLUME = "Voice volume"; AAS_ENABLE_ALL = "Enable all"; AAS_DISABLE_ALL = "Disable all"; AAS_EVENT_all_kill = "All enemies down"; AAS_EVENT_boss_kill = "Boss down"; AAS_EVENT_group_kill = "No active enemies"; AAS_EVENT_group_alert = "New active enemies"; AAS_EVENT_boss_alert = "Boss alerted"; AAS_EVENT_all_items_found = "All items found"; AAS_EVENT_time_period = "Time passed"; AAS_EVENT_teleport = "Moved to another place"; AAS_EVENT_manual = "Manual save"; AAS_EVENT_important = "Something important!"; AAS_EVENT_level_start = "Level started"; AAS_EVENT_one_percent = "Last health percent!"; AAS_EVENT_enemies_10 = "Enemies 10% down"; AAS_EVENT_enemies_20 = "Enemies 20% down"; AAS_EVENT_enemies_30 = "Enemies 30% down"; AAS_EVENT_enemies_40 = "Enemies 40% down"; AAS_EVENT_enemies_50 = "Enemies 50% down"; AAS_EVENT_enemies_60 = "Enemies 60% down"; AAS_EVENT_enemies_70 = "Enemies 70% down"; AAS_EVENT_enemies_80 = "Enemies 80% down"; AAS_EVENT_enemies_90 = "Enemies 90% down"; AAS_EVENT_health_drop = "Health low"; AAS_EVENT_health_rise = "Health high"; AAS_EVENT_armor_drop = "Armor low"; AAS_EVENT_armor_rise = "Armor high"; AAS_EVENT_secret_found = "Secret found"; AAS_EVENT_powerup = "Powerup found"; AAS_EVENT_weapon = "Weapon found"; AAS_EVENT_key = "Key found"; AAS_EVENT_backpack = "Backpack found"; AAS_EVENT_new_armor = "New armor"; AAS_EVENT_big_heal = "Major healing"; AAS_EVENT_armor = "Armor found";
8.2. Brazilian Portuguese
// SPDX-FileCopyrightText: © 2022 generic name guy [pt] AAS_MANUAL_CONTROL_NAME = "Autosave manual"; AAS_TOGGLE_CONTROL_NAME = "Ligar Autoautosave"; AAS_TOGGLE_ON_MESSAGE = "Autoautosave ligado"; AAS_TOGGLE_OFF_MESSAGE = "Autoautosave desligado"; AAS_LEVEL_NOTHING = "nada"; AAS_LEVEL_QUIET = "quieto"; AAS_LEVEL_VERBOSE = "verboso"; AAS_LEVEL_EVERYTHING = "tudo"; AAS_MUST_ALWAYS = "Isto deve estar definido como"; AAS_GLOBAL_TOGGLE = "Ligar Mod"; AAS_CONTROLS = "Controles"; AAS_EVENT_TUNING_TITLE = "Opções de Evento"; AAS_REPORT_OPTIONS_TITLE = "Opções de registramento"; AAS_INDIVIDUAL_TOGGLES_TITLE = "Opções de Autosave"; AAS_SCREENSHOT_TOGGLES_TITLE = "Opções de Captura de Tela"; AAS_VOICE_OPTIONS_TITLE = "Opções de Voz"; AAS_SCREENSHOTS_WARNING = "Aviso: Muitas capturas de tela podem usar espaço no disco."; AAS_SCREENSHOTS_SCRIPT_NOTE = "Isto deve estar definido como \"Sim\"."; AAS_LOG_CONSOLE = "Registrar no Console"; AAS_LOG_SCREEN = "Registrar na Tela"; AAS_PRINT_COLOR = "Cor do registro"; AAS_LOG_QUIET_NOTE_1 = "Quieto:"; AAS_LOG_QUIET_NOTE_2 = "Inimigos, tempo, teleporte, ítens."; AAS_LOG_VERBOSE_NOTE_1 = "Verboso:"; AAS_LOG_VERBOSE_NOTE_2 = "Inimigos, tempo, teleporte, ítens, % de vítmas, vida, armadura."; AAS_MIN_BOSS_HEALTH = "Inimigo é um chefão quando a vida for"; AAS_GROUP_NUMBER = "Grupo é pelo menos N inimigos"; AAS_BOSS_GROUP_NUMBER = "Grupo de chefão é pelo menos N inimigos"; AAS_HEALTH_LIMIT = "Não salve quando a vida for menos de"; AAS_BIG_HEAL_SIZE = "Tanto de cura grande"; AAS_LOW_HEALTH_THRESHOLD = "Cota de vida baoxa"; AAS_HIGH_HEALTH_THRESHOLD = "Cota de vida alta"; AAS_LOW_ARMOR_THRESHOLD = "Cota de armadura baixa"; AAS_HIGH_ARMOR_THRESHOLD = "Cota de armadura alta"; AAS_MIN_SAVE_WAIT = "Não salve até segundos passados"; AAS_PERIOD = "Autosalvar todos N segundos"; AAS_SAVE_ON_DROPPED = "Salvar nos ítens derrubados"; AAS_SAVE_ON_DROPPED_NOTE = "Também pegando chaves do Hideous Destructor"; AAS_VOICE_VOLUME = "Volume de voz"; AAS_ENABLE_ALL = "Habilitar todos"; AAS_DISABLE_ALL = "Desabilitar todos"; AAS_EVENT_all_kill = "Todos os inimigos foram eliminados"; AAS_EVENT_boss_kill = "O chefão foi eliminado"; AAS_EVENT_group_kill = "Sem mais inimigos ativos (abate em grupo)"; AAS_EVENT_group_alert = "Novos inimigos ativos (alerta de grupo)"; AAS_EVENT_boss_alert = "O chefão foi alertado"; AAS_EVENT_all_items_found = "Todos os ítens foram descobertos"; AAS_EVENT_time_period = "Tempo movido"; AAS_EVENT_teleport = "Você foi movido para outro lugar"; AAS_EVENT_manual = "Save manual"; AAS_EVENT_important = "Algo importante!"; AAS_EVENT_level_start = "Nível começado"; AAS_EVENT_one_percent = "Sua vida caiu para 1%"; AAS_EVENT_enemies_10 = "Inimigos 10% mortos"; AAS_EVENT_enemies_20 = "Inimigos 20% mortos"; AAS_EVENT_enemies_30 = "Inimigos 30% mortos"; AAS_EVENT_enemies_40 = "Inimigos 40% mortos"; AAS_EVENT_enemies_50 = "Inimigos 50% mortos"; AAS_EVENT_enemies_60 = "Inimigos 60% mortos"; AAS_EVENT_enemies_70 = "Inimigos 70% mortos"; AAS_EVENT_enemies_80 = "Inimigos 80% mortos"; AAS_EVENT_enemies_90 = "Inimigos 90% mortos"; AAS_EVENT_health_drop = "Sua vida caiu baixo"; AAS_EVENT_health_rise = "Sua vida subiu alto"; AAS_EVENT_armor_drop = "Sua armadura caiu baixo"; AAS_EVENT_armor_rise = "Sua armadura subiu alto"; AAS_EVENT_secret_found = "Um segredo foi encontrado"; AAS_EVENT_powerup = "Um poder foi encontrado"; AAS_EVENT_weapon = "Uma arma foi encontrada"; AAS_EVENT_key = "Uma chave foi encontrada"; AAS_EVENT_backpack = "Uma mochila foi encontrada"; AAS_EVENT_new_armor = "Novo tipo de armadura"; AAS_EVENT_big_heal = "Grande cura"; AAS_EVENT_armor = "Armadura foi encontrada";
9. Sounds
$pitchshiftrange 0 aas/voice0 "sounds/aas0.ogg" aas/voice1 "sounds/aas1.ogg" aas/voice2 "sounds/aas2.ogg" aas/voice3 "sounds/aas3.ogg" aas/voice4 "sounds/aas4.ogg" aas/voice5 "sounds/aas5.ogg" aas/voice7 "sounds/aas7.ogg" aas/voice8 "sounds/aas8.ogg" aas/voice9 "sounds/aas9.ogg" aas/voice11 "sounds/aas11.ogg" aas/voice12 "sounds/aas12.ogg" aas/voice13 "sounds/aas13.ogg" aas/voice14 "sounds/aas14.ogg" aas/voice15 "sounds/aas15.ogg" aas/voice16 "sounds/aas16.ogg" aas/voice17 "sounds/aas17.ogg" aas/voice18 "sounds/aas18.ogg" aas/voice19 "sounds/aas19.ogg" aas/voice20 "sounds/aas20.ogg" aas/voice21 "sounds/aas21.ogg" aas/voice22 "sounds/aas22.ogg" aas/voice100 "sounds/aas100.ogg" aas/voice101 "sounds/aas101.ogg" aas/voice102 "sounds/aas102.ogg" aas/voice103 "sounds/aas103.ogg" aas/voice104 "sounds/aas104.ogg" aas/voice200 "sounds/aas200.ogg" aas/voice201 "sounds/aas201.ogg" aas/voice202 "sounds/aas202.ogg" aas/voice203 "sounds/aas203.ogg" aas/voice204 "sounds/aas204.ogg" aas/voice205 "sounds/aas205.ogg" aas/voice206 "sounds/aas206.ogg"
10. Source
10.1. Event
class aas_event { enum _ { all_kill = 0, boss_kill = 1, group_kill = 2, group_alert = 3, boss_alert = 4, all_items_found = 5, time_period = 7, teleport = 8, manual = 9, important = 11, level_start = 12, one_percent = 13, enemies_10 = 14, enemies_20 = 15, enemies_30 = 16, enemies_40 = 17, enemies_50 = 18, enemies_60 = 19, enemies_70 = 20, enemies_80 = 21, enemies_90 = 22, health_drop = 100, health_rise = 101, armor_drop = 102, armor_rise = 103, secret_found = 104, powerup = 200, weapon = 201, key = 202, backpack = 203, new_armor = 204, big_heal = 205, armor = 206, } static string message(int type) { switch (type) { case all_kill : return StringTable.localize("$aas_event_all_kill"); case boss_kill : return StringTable.localize("$aas_event_boss_kill"); case group_kill : return StringTable.localize("$aas_event_group_kill"); case group_alert : return StringTable.localize("$aas_event_group_alert"); case boss_alert : return StringTable.localize("$aas_event_boss_alert"); case all_items_found : return StringTable.localize("$aas_event_all_items_found"); case time_period : return StringTable.localize("$aas_event_time_period"); case teleport : return StringTable.localize("$aas_event_teleport"); case manual : return StringTable.localize("$aas_event_manual"); case important : return StringTable.localize("$aas_event_important"); case level_start : return StringTable.localize("$aas_event_level_start"); case one_percent : return StringTable.localize("$aas_event_one_percent"); case enemies_10 : return StringTable.localize("$aas_event_enemies_10"); case enemies_20 : return StringTable.localize("$aas_event_enemies_20"); case enemies_30 : return StringTable.localize("$aas_event_enemies_30"); case enemies_40 : return StringTable.localize("$aas_event_enemies_40"); case enemies_50 : return StringTable.localize("$aas_event_enemies_50"); case enemies_60 : return StringTable.localize("$aas_event_enemies_60"); case enemies_70 : return StringTable.localize("$aas_event_enemies_70"); case enemies_80 : return StringTable.localize("$aas_event_enemies_80"); case enemies_90 : return StringTable.localize("$aas_event_enemies_90"); case health_drop : return StringTable.localize("$aas_event_health_drop"); case health_rise : return StringTable.localize("$aas_event_health_rise"); case armor_drop : return StringTable.localize("$aas_event_armor_drop"); case armor_rise : return StringTable.localize("$aas_event_armor_rise"); case secret_found : return StringTable.localize("$aas_event_secret_found"); case powerup : return StringTable.localize("$aas_event_powerup"); case weapon : return StringTable.localize("$aas_event_weapon"); case key : return StringTable.localize("$aas_event_key"); case backpack : return StringTable.localize("$aas_event_backpack"); case new_armor : return StringTable.localize("$aas_event_new_armor"); case big_heal : return StringTable.localize("$aas_event_big_heal"); case armor : return StringTable.localize("$aas_event_armor"); default: aas_log.error(string.format("unknown event: %d", type)); return "Unknown event type."; } } static string toggle_name(int type) { switch (type) { case all_kill : return "aas_save_on_all_kill"; case boss_kill : return "aas_save_on_boss_kill"; case group_kill : return "aas_save_on_group_kill"; case group_alert : return "aas_save_on_group_alert"; case boss_alert : return "aas_save_on_boss_alert"; case all_items_found : return "aas_save_on_all_items_found"; case time_period : return "aas_save_on_time_period"; case teleport : return "aas_save_on_teleport"; case manual : return "aas_save_on_manual"; case important : return "aas_save_on_important"; case level_start : return "aas_save_on_level_start"; case one_percent : return "aas_save_on_one_percent"; case enemies_10 : return "aas_save_on_enemies_10"; case enemies_20 : return "aas_save_on_enemies_20"; case enemies_30 : return "aas_save_on_enemies_30"; case enemies_40 : return "aas_save_on_enemies_40"; case enemies_50 : return "aas_save_on_enemies_50"; case enemies_60 : return "aas_save_on_enemies_60"; case enemies_70 : return "aas_save_on_enemies_70"; case enemies_80 : return "aas_save_on_enemies_80"; case enemies_90 : return "aas_save_on_enemies_90"; case health_drop : return "aas_save_on_health_drop"; case health_rise : return "aas_save_on_health_rise"; case armor_drop : return "aas_save_on_armor_drop"; case armor_rise : return "aas_save_on_armor_rise"; case secret_found : return "aas_save_on_secret_found"; case powerup : return "aas_save_on_powerup"; case weapon : return "aas_save_on_weapon"; case key : return "aas_save_on_key"; case backpack : return "aas_save_on_backpack"; case new_armor : return "aas_save_on_new_armor"; case big_heal : return "aas_save_on_big_heal"; case armor : return "aas_save_on_armor"; default: aas_log.error(string.format("unknown event: %d", type)); return "aas_unknown"; } } static string screenshot_toggle_name(int type) { switch (type) { case all_kill : return "aas_screenshot_on_all_kill"; case boss_kill : return "aas_screenshot_on_boss_kill"; case group_kill : return "aas_screenshot_on_group_kill"; case group_alert : return "aas_screenshot_on_group_alert"; case boss_alert : return "aas_screenshot_on_boss_alert"; case all_items_found : return "aas_screenshot_on_all_items_found"; case time_period : return "aas_screenshot_on_time_period"; case teleport : return "aas_screenshot_on_teleport"; case manual : return "aas_screenshot_on_manual"; case important : return "aas_screenshot_on_important"; case level_start : return "aas_screenshot_on_level_start"; case one_percent : return "aas_screenshot_on_one_percent"; case enemies_10 : return "aas_screenshot_on_enemies_10"; case enemies_20 : return "aas_screenshot_on_enemies_20"; case enemies_30 : return "aas_screenshot_on_enemies_30"; case enemies_40 : return "aas_screenshot_on_enemies_40"; case enemies_50 : return "aas_screenshot_on_enemies_50"; case enemies_60 : return "aas_screenshot_on_enemies_60"; case enemies_70 : return "aas_screenshot_on_enemies_70"; case enemies_80 : return "aas_screenshot_on_enemies_80"; case enemies_90 : return "aas_screenshot_on_enemies_90"; case health_drop : return "aas_screenshot_on_health_drop"; case health_rise : return "aas_screenshot_on_health_rise"; case armor_drop : return "aas_screenshot_on_armor_drop"; case armor_rise : return "aas_screenshot_on_armor_rise"; case secret_found : return "aas_screenshot_on_secret_found"; case powerup : return "aas_screenshot_on_powerup"; case weapon : return "aas_screenshot_on_weapon"; case key : return "aas_screenshot_on_key"; case backpack : return "aas_screenshot_on_backpack"; case new_armor : return "aas_screenshot_on_new_armor"; case big_heal : return "aas_screenshot_on_big_heal"; case armor : return "aas_screenshot_on_armor"; default: aas_log.error(string.format("unknown event: %d", type)); return "Unknown event type."; } } static string voice_toggle_name(int type) { switch (type) { case all_kill : return "aas_voice_on_all_kill"; case boss_kill : return "aas_voice_on_boss_kill"; case group_kill : return "aas_voice_on_group_kill"; case group_alert : return "aas_voice_on_group_alert"; case boss_alert : return "aas_voice_on_boss_alert"; case all_items_found : return "aas_voice_on_all_items_found"; case time_period : return "aas_voice_on_time_period"; case teleport : return "aas_voice_on_teleport"; case manual : return "aas_voice_on_manual"; case important : return "aas_voice_on_important"; case level_start : return "aas_voice_on_level_start"; case one_percent : return "aas_voice_on_one_percent"; case enemies_10 : return "aas_voice_on_enemies_10"; case enemies_20 : return "aas_voice_on_enemies_20"; case enemies_30 : return "aas_voice_on_enemies_30"; case enemies_40 : return "aas_voice_on_enemies_40"; case enemies_50 : return "aas_voice_on_enemies_50"; case enemies_60 : return "aas_voice_on_enemies_60"; case enemies_70 : return "aas_voice_on_enemies_70"; case enemies_80 : return "aas_voice_on_enemies_80"; case enemies_90 : return "aas_voice_on_enemies_90"; case health_drop : return "aas_voice_on_health_drop"; case health_rise : return "aas_voice_on_health_rise"; case armor_drop : return "aas_voice_on_armor_drop"; case armor_rise : return "aas_voice_on_armor_rise"; case secret_found : return "aas_voice_on_secret_found"; case powerup : return "aas_voice_on_powerup"; case weapon : return "aas_voice_on_weapon"; case key : return "aas_voice_on_key"; case backpack : return "aas_voice_on_backpack"; case new_armor : return "aas_voice_on_new_armor"; case big_heal : return "aas_voice_on_big_heal"; case armor : return "aas_voice_on_armor"; default: aas_log.error(string.format("unknown event: %d", type)); return "Unknown event type."; } } }
10.1.1. Test
class aas_event_test : Clematis { override void testSuites() { describe("Testing event messages"); it("all_kill has string", Assert(aas_event.message(0) != "aas_event_all_kill")); it("boss_kill has string", Assert(aas_event.message(1) != "aas_event_boss_kill")); it("group_kill has string", Assert(aas_event.message(2) != "aas_event_group_kill")); it("group_alert has string", Assert(aas_event.message(3) != "aas_event_group_alert")); it("boss_alert has string", Assert(aas_event.message(4) != "aas_event_boss_alert")); it("all_items_found has string", Assert(aas_event.message(5) != "aas_event_all_items_found")); it("time_period has string", Assert(aas_event.message(7) != "aas_event_time_period")); it("teleport has string", Assert(aas_event.message(8) != "aas_event_teleport")); it("manual has string", Assert(aas_event.message(9) != "aas_event_manual")); it("important has string", Assert(aas_event.message(11) != "aas_event_important")); it("level_start has string", Assert(aas_event.message(12) != "aas_event_level_start")); it("one_percent has string", Assert(aas_event.message(13) != "aas_event_one_percent")); it("enemies_10 has string", Assert(aas_event.message(14) != "aas_event_enemies_10")); it("enemies_20 has string", Assert(aas_event.message(15) != "aas_event_enemies_20")); it("enemies_30 has string", Assert(aas_event.message(16) != "aas_event_enemies_30")); it("enemies_40 has string", Assert(aas_event.message(17) != "aas_event_enemies_40")); it("enemies_50 has string", Assert(aas_event.message(18) != "aas_event_enemies_50")); it("enemies_60 has string", Assert(aas_event.message(19) != "aas_event_enemies_60")); it("enemies_70 has string", Assert(aas_event.message(20) != "aas_event_enemies_70")); it("enemies_80 has string", Assert(aas_event.message(21) != "aas_event_enemies_80")); it("enemies_90 has string", Assert(aas_event.message(22) != "aas_event_enemies_90")); it("health_drop has string", Assert(aas_event.message(100) != "aas_event_health_drop")); it("health_rise has string", Assert(aas_event.message(101) != "aas_event_health_rise")); it("armor_drop has string", Assert(aas_event.message(102) != "aas_event_armor_drop")); it("armor_rise has string", Assert(aas_event.message(103) != "aas_event_armor_rise")); it("secret_found has string", Assert(aas_event.message(104) != "aas_event_secret_found")); it("powerup has string", Assert(aas_event.message(200) != "aas_event_powerup")); it("weapon has string", Assert(aas_event.message(201) != "aas_event_weapon")); it("key has string", Assert(aas_event.message(202) != "aas_event_key")); it("backpack has string", Assert(aas_event.message(203) != "aas_event_backpack")); it("new_armor has string", Assert(aas_event.message(204) != "aas_event_new_armor")); it("big_heal has string", Assert(aas_event.message(205) != "aas_event_big_heal")); it("armor has string", Assert(aas_event.message(206) != "aas_event_armor")); endDescribe(); } }
10.2. Precache
class aas_precache_sounds { static void precache_sounds() { markSound("aas/voice0"); markSound("aas/voice1"); markSound("aas/voice2"); markSound("aas/voice3"); markSound("aas/voice4"); markSound("aas/voice5"); markSound("aas/voice7"); markSound("aas/voice8"); markSound("aas/voice9"); markSound("aas/voice11"); markSound("aas/voice12"); markSound("aas/voice13"); markSound("aas/voice14"); markSound("aas/voice15"); markSound("aas/voice16"); markSound("aas/voice17"); markSound("aas/voice18"); markSound("aas/voice19"); markSound("aas/voice20"); markSound("aas/voice21"); markSound("aas/voice22"); markSound("aas/voice100"); markSound("aas/voice101"); markSound("aas/voice102"); markSound("aas/voice103"); markSound("aas/voice104"); markSound("aas/voice200"); markSound("aas/voice201"); markSound("aas/voice202"); markSound("aas/voice203"); markSound("aas/voice204"); markSound("aas/voice205"); markSound("aas/voice206"); } }
10.3. Cvar
// This class provides access to a user or server Cvar. // // Accessing Cvars through this class is faster because calling Cvar.GetCvar() // is costly. This class caches the result of Cvar.GetCvar() and handles // loading a savegame. class aas_cvar { static aas_cvar of(string name, PlayerInfo player = NULL) { let result = new("aas_cvar"); result._player = player; result._name = name; result._cvar = NULL; result.load(); return result; } string get_string() { if (_cvar == NULL) load(); return _cvar.GetString(); } bool get_bool() { if (_cvar == NULL) load(); return _cvar.GetInt(); } int get_int() { if (_cvar == NULL) load(); return _cvar.GetInt(); } double get_double() { if (_cvar == NULL) load(); return _cvar.GetFloat(); } private void load() { _cvar = Cvar.GetCvar(_name, _player); if (_cvar == NULL) { aas_log.error(string.Format("cvar %s not found", _name)); } } private PlayerInfo _player; private string _name; private transient Cvar _cvar; }
10.4. Event source
// This class represents the core of Autoautosave. class aas_event_source play { static aas_event_source of() { let result = new("aas_event_source"); let last_save_time = aas_timestamp.of(); result._clock = aas_level_clock.of(); result._scheduler = aas_game_action_scheduler.of(); result._voice = aas_voice.of(); result._saver = aas_saver.of(result._scheduler, result._clock, last_save_time); Array<aas_event_handler> handlers = { result._saver, aas_logger.of(aas_log_function.of(), "aas_console_log_level"), aas_logger.of(aas_print_function.of(aas_Fonts.findSmall()), "aas_screen_level"), result._voice, aas_screenshot_maker.of(result._scheduler) }; result._handler = aas_event_handlers.of(handlers); result._save_timer = aas_save_timer.of(result._clock, last_save_time); result._old_kill_count = level.killed_monsters; result._old_item_count = level.found_items; PlayerInfo player_info = players[consolePlayer]; let player = PlayerPawn(player_info.mo); result._old_pos = player.Pos; result._old_health = player.Health; result._old_armor = player.CountInv("BasicArmor"); result._old_secret_count = player_info.secretCount; BasicArmor armor = BasicArmor(player.FindInventory("BasicArmor")); if (armor) { result._old_armor_save = armor.SavePercent; } else { result._old_armor_save = 0.0; } result._save_on_dropped = aas_cvar.of("aas_save_on_dropped"); result._health_down = aas_cvar.of("aas_health_threshold_down"); result._health_up = aas_cvar.of("aas_health_threshold_up"); result._armor_down = aas_cvar.of("aas_armor_threshold_down"); result._armor_up = aas_cvar.of("aas_armor_threshold_up"); result._major_healing = aas_cvar.of("aas_big_heal"); let active_enemies_counter = aas_active_enemies_counter.of(); let enemies_group_cvar = aas_cvar.of("aas_group_number"); result._active_enemies_checker = aas_active_enemies_checker.of( result._handler , active_enemies_counter , enemies_group_cvar , aas_event.group_alert , aas_event.group_kill ); let active_bosses_counter = aas_active_bosses_counter.of(); let bosses_group_cvar = aas_cvar.of("aas_boss_group_number"); result._active_bosses_checker = aas_active_enemies_checker.of( result._handler , active_bosses_counter , bosses_group_cvar , aas_event.boss_alert , aas_event.boss_kill ); return result; } void on_event(int event_type) { _handler.on_event(event_type); } void tick() { if (_save_timer.is_periodic_save() && players[consolePlayer].mo.health > 0) { on_event(aas_event.time_period); } _voice.tick(); _active_enemies_checker.tick(); _active_bosses_checker.tick(); check_secrets(); check_all_found(); check_player_events(); check_all_killed(); check_kill_percents(); _old_kill_count = level.killed_monsters; _scheduler.tick(); } void on_thing_spawned(Actor item) { string class_name = item.getClassName(); if (aas_su.beginsWith(class_name, "aas_")) { if (aas_su.beginsWith(class_name, "aas_pickup_")) aas_token(Actor.spawn("aas_token", item.pos)).init(aas_event.important, _handler, NULL); else if (aas_su.beginsWith(class_name, "aas_instant_")) on_event(aas_event.important); else if (aas_su.beginsWith(class_name, "aas_silent_instant_")) _saver.on_event(aas_event.important); return; } if (!_save_on_dropped.get_bool() && is_loading_finished()) { return; } static const string saving_item_classes[] = { "Key", // keys "FDKeyBase", "QCRedCard", "QCYellowCard", "QCBlueCard", "QCRedSkull", "QCYellowSkull", "QCBlueSkull", "Weapon", // weapons "Goonades", "SPAMMineItem", "PowerupGiver", // powerups "TBPowerupBase", "MapRevealer", "Berserk", "Speeders", "IcarusMk8", "ProtectoBand", "BigSPAMMine", "GuardBoi", "BadassGlasses", "Mapisto", "LovebirdTag", "BigScorePresent", "BackpackItem", // backpacks "Backpack2", "BlueprintItem", "NetronianBackpack", "Big_Coin_pickup", // other "BasicArmorPickup" // armor }; static const int types[] = { aas_event.key, aas_event.key, aas_event.key, aas_event.key, aas_event.key, aas_event.key, aas_event.key, aas_event.key, aas_event.weapon, aas_event.weapon, aas_event.weapon, aas_event.powerup, aas_event.powerup, aas_event.powerup, aas_event.powerup, aas_event.powerup, aas_event.powerup, aas_event.powerup, aas_event.powerup, aas_event.powerup, aas_event.powerup, aas_event.powerup, aas_event.powerup, aas_event.powerup, aas_event.backpack, aas_event.backpack, aas_event.backpack, aas_event.backpack, aas_event.important, aas_event.armor }; int n_saving_items_classes = saving_item_classes.size(); if (n_saving_items_classes != types.size()) { aas_log.error("invalid saving items"); } Actor player = players[consolePlayer].mo; Actor owner = (item is "Inventory") ? Inventory(item).owner : NULL; for (int i = 0; i < n_saving_items_classes; ++i) { string saving_class = saving_item_classes[i]; if (!(item is saving_class || Actor.GetReplacee(class_name) is saving_class)) { continue; } if (owner == NULL) { aas_token(Actor.Spawn("aas_token", item.pos)).init(types[i], _handler, item); } else if (owner == player && is_loading_finished()) { // don't save on obtaining starting weapons string netronianBackpack = "NetronianBackpack"; class<Actor> netronianBackpackClass = netronianBackpack; // don't save on BackpackItem for Netronian Chaos, save on // Netronian Backpack instead if (saving_class == "BackpackItem" && netronianBackpackClass) { return; } on_event(types[i]); } break; } } private void check_player_events() { PlayerInfo player = players[consolePlayer]; int health = player.mo.health; if (health <= 0) return; { vector3 pos = player.mo.Pos; float x_diff = (pos.x - _old_pos.x); float y_diff = (pos.y - _old_pos.y); float dist = x_diff * x_diff + y_diff * y_diff; if (dist > 2000000.0) { aas_log.debug(string.Format("positions distance: %f", dist)); on_event(aas_event.teleport); } _old_pos = pos; } { int health_down = _health_down.get_int(); int health_up = _health_up.get_int(); if (health < health_down && _old_health >= health_down) { on_event(aas_event.health_drop); } else if (health > health_up && _old_health <= health_up) { on_event(aas_event.health_rise); } else if (health >= _old_health + _major_healing.get_int() && _old_health > 0) { on_event(aas_event.big_heal); } if (health == 1 && _old_health > 1) { on_event(aas_event.one_percent); } _old_health = health; } { int armor_count = player.mo.CountInv("BasicArmor"); int armor_down = _armor_down.get_int(); int armor_up = _armor_up.get_int(); if (armor_count < armor_down && _old_armor >= armor_down && health > 0) { on_event(aas_event.armor_drop); } else if (armor_count > armor_up && _old_armor <= armor_up) { on_event(aas_event.armor_rise); } _old_armor = armor_count; } { BasicArmor armor = BasicArmor(player.mo.FindInventory("BasicArmor")); if (armor != NULL) { double save_percent = armor.SavePercent; if (save_percent != 0.0 && save_percent != _old_armor_save) { on_event(aas_event.new_armor); } _old_armor_save = save_percent; } } } private void check_secrets() { PlayerInfo player = players[consolePlayer]; int secret_count = player.secretCount; if (secret_count > _old_secret_count) { on_event(aas_event.secret_found); } _old_secret_count = secret_count; } private void check_all_killed() { int kill_count = level.killed_monsters; if (kill_count != _old_kill_count && kill_count == level.total_monsters) { on_event(aas_event.all_kill); } } private void check_kill_percents() { int kill_count = level.killed_monsters; static const int events[] = { aas_event.enemies_10, aas_event.enemies_20, aas_event.enemies_30, aas_event.enemies_40, aas_event.enemies_50, aas_event.enemies_60, aas_event.enemies_70, aas_event.enemies_80, aas_event.enemies_90 }; for (int i = 0; i < 9; ++i) { int percent = (i + 1) * 10; int ratio = int(ceil(level.total_monsters * percent / 100.0)); if (_old_kill_count < ratio && kill_count >= ratio) { on_event(events[i]); } } } private void check_all_found() { int item_count = level.found_items; if (item_count != _old_item_count && item_count == level.total_items) { on_event(aas_event.all_items_found); } _old_item_count = item_count; } private bool is_loading_finished() { return (_clock.time() > 0); } private aas_clock _clock; private aas_event_handler _handler; private aas_game_action_scheduler _scheduler; private aas_save_timer _save_timer; private aas_voice _voice; private aas_event_handler _saver; private int _old_kill_count; private int _old_item_count; private int _old_secret_count; private vector3 _old_pos; private int _old_health; private int _old_armor; private double _old_armor_save; private aas_cvar _save_on_dropped; private aas_cvar _health_down; private aas_cvar _health_up; private aas_cvar _armor_down; private aas_cvar _armor_up; private aas_cvar _major_healing; private aas_active_enemies_checker _active_enemies_checker; private aas_active_enemies_checker _active_bosses_checker; }
10.5. Function
// Makeshift functor object. class aas_function abstract { abstract void f1(string s); }
10.6. Game action scheduler
class aas_game_action_scheduler : aas_game_actions { static aas_game_action_scheduler of() { return new("aas_game_action_scheduler"); } override void save() { _is_save_scheduled = true; } override void take_screenshot() { _is_screenshot_scheduled = true; } play void tick() { if (gameAction != ga_nothing) { return; } if (_is_screenshot_scheduled) { LevelLocals.MakeScreenShot(); _is_screenshot_scheduled = false; return; } if (_is_save_scheduled) { LevelLocals.MakeAutoSave(); _is_save_scheduled = false; return; } } private bool _is_save_scheduled; private bool _is_screenshot_scheduled; }
10.7. Game actions
class aas_game_actions abstract { abstract void save(); abstract void take_screenshot(); }
10.8. Log
class aas_log { static void print(string s) { Console.printf("%s", StringTable.Localize(s, false)); } static void error(string s) { Console.printf("ERROR: Autoautosave: %s.", s); } static void debug(string s) { if (NO_DEBUG) return; Console.printf("DEBUG: Autoautosave: %s.", s); } const NO_DEBUG = 1; // == true }
10.9. Main
// This is an entry point for Autoautosave. class aas_main : EventHandler { override void NetworkProcess(ConsoleEvent event) { Array<string> command; event.Name.Split(command, ":"); // All known commands have 1 argument. if (command.Size() != 2) return; if (command[0] == "aas_manual_save") { int event_type = command[1].ToInt(); _event_source.on_event(event_type); } else if (command[0] == "aas_log") { aas_log.print(command[1]); } } override void PlayerEntered(PlayerEvent event) { if (is_titlemap()) { destroy(); return; } if (event.PlayerNumber != consolePlayer) return; aas_precache_sounds.precache_sounds(); _event_source = aas_event_source.of(); _event_source.on_event(aas_event.level_start); } override void WorldThingSpawned(WorldEvent event) { if (event == NULL || event.thing == NULL) return; _event_source.on_thing_spawned(event.thing); } override void WorldTick() { if (_event_source) { _event_source.tick(); } } private static bool is_titlemap() { return (level.mapName ~== "TITLEMAP"); } private aas_event_source _event_source; }
10.10. Save timer
class aas_save_timer play { static aas_save_timer of(aas_clock clock, aas_timestamp timestamp) { let result = new("aas_save_timer"); result._clock = clock; result._last_save_time = timestamp; result._autosave_period_s = aas_cvar.of("aas_autosave_period"); result._save_on_time_period = aas_cvar.of("aas_save_on_time_period"); return result; } bool is_periodic_save() { int time_from_last_save = _clock.time() - _last_save_time.get_time(); int autosave_period = _autosave_period_s.get_int() * TICRATE; bool is_period = (time_from_last_save % autosave_period) == (autosave_period - 1); return is_period && _save_on_time_period.get_int() && _clock.time() > 0; } private aas_clock _clock; private aas_timestamp _last_save_time; private aas_cvar _autosave_period_s; private aas_cvar _save_on_time_period; }
10.11. Timestamp
// Represents a moment in time, in game ticks. class aas_timestamp { static aas_timestamp of() { let result = new("aas_timestamp"); result._time = 0; return result; } void set_time(int time) { _time = time; } int get_time() { return _time; } private int _time; }
10.12. Token
// Creates an aas_event when pickup up. class aas_token : Inventory { Default { +Inventory.Quiet; +DontGib; } aas_token init(int type, aas_event_handler handler, Actor origin) { _event_type = type; _handler = handler; _origin = origin; _connected = origin != NULL; return self; } override bool TryPickup(out Actor toucher) { if (_handler != NULL) _handler.on_event(_event_type); GoAwayAndDie(); return true; } override void tick() { Super.tick(); if (!_connected) return; if (_origin == NULL) { destroy(); return; } setOrigin(_origin.pos, true); a_setSize(_origin.radius, _origin.height); } private int _event_type; private Actor _origin; private bool _connected; private aas_event_handler _handler; }
10.13. Universal token examples
// Universal Autoautosave Tokens can be used by map and mod authors. When a // player walks over such a token, it causes a generic Autoautosave event (with // a message like "Something important!"). // In settings, saves caused by tokens fall into "Important" category. // // Typically, Universal Autoautosave Token is an invisible object. // // The advantage of saving via a Universal Autoautosave Token instead of other // saving scripts is that a token can cause a text or voice message and a // screenshot according to player settings. Also, Autoautosave framework ensures // that saves aren't happening too often, if the player so desires. // // A mod that utilizes Universal Autoautosave Tokens doesn't depend on // Autoautosave and can be loaded without it. // // Universal Autosave Tokens come it two flavors: // 1. Pickup Tokens // 2. Instant Tokens // 3. Silent Instant Tokens // // How it works: Autoautosave watches for things spawns, and if the beginning of // the class name of the spawned thing matches one of the signatures, // Autoautosave acts accordingly. // This is an example of Pickup Universal Autoautosave Token. // // How to use: // 1. Define a class that starts with "aas_pickup_"; // 2. Spawn this class on a spot, and autoautosave will happen when the player // walks over this spot. class aas_pickup_example : Actor { // Universal Autoautosave Tokens don't have to exist after being spawned. States { Spawn: TNT1 A 0; stop; } } // This is an example of Instant Universal Autoautosave Token. // // How to use: // 1. Define a class that starts with "aas_instant_"; // 2. Spawn this class, and autoautosave happens immediately. class aas_instant_example : Actor { // These tokens don't have to exist after being spawned. States { Spawn: TNT1 A 0; stop; } } // This is an example of Silent Instant Universal Autoautosave Token. // // How to use: // 1. Define a class that starts with "aas_instant_"; // 2. Spawn this class, and autoautosave happens immediately, without playing // the voice notification, and without printing to console and to screen. class aas_silent_instant_example : Actor { // These tokens don't have to exist after being spawned. States { Spawn: TNT1 A 0; stop; } }
10.14. Active enemies checker
// This checker watches for active enemies numbers. When active enemies number // exceeds the number specified in aas_group_number CVar, group_alert event // is emitted. When active number reaches zero, group_kill event is emitted, but // only if active group of enemies was detected before. // // There is a problem with enemies activating. They don't wake up immediately // when they see or hear the player, only when A_Look function happens in their // state. This class manages this problem by keeping a short history of // activated enemy counts, and setting a timeout before emitting the next // group_alert event. class aas_active_enemies_checker { static aas_active_enemies_checker of(aas_event_handler handler, aas_enemies_counter counter, aas_cvar group_number, int alert_event, int kill_event) { let result = new("aas_active_enemies_checker"); result._handler = handler; result._counter = counter; result._group_number = group_number; result._alert_event = alert_event; result._kill_event = kill_event; result._is_group = false; result._old_active_count = 0; result._activity_statistics = aas_ring_buffer.of(); return result; } void tick() { if (is_player_dead()) return; int active_enemies_count = _counter.count(); int group_count = _group_number.get_int(); int newly_active_enemies = max(0, active_enemies_count - _old_active_count); _old_active_count = active_enemies_count; _timeout = max(0, _timeout - 1); if (_timeout == 0) { _activity_statistics.write(newly_active_enemies); } int recently_active_enemies = _activity_statistics.get_sum(); if (recently_active_enemies >= group_count && _timeout == 0) { _is_group = true; _activity_statistics.clear(); _timeout = TIMEOUT; on_event(_alert_event); } else if (active_enemies_count == 0 && _is_group) { _is_group = false; _activity_statistics.clear(); on_event(_kill_event); } } private void on_event(int event_type) { _handler.on_event(event_type); } private bool is_player_dead() { return players[consolePlayer].Health <= 0; } private aas_cvar _group_number; private int _is_group; private int _old_active_count; private aas_event_handler _handler; private aas_enemies_counter _counter; private aas_ring_buffer _activity_statistics; const TIMEOUT = 20; private int _timeout; private int _alert_event; private int _kill_event; } class aas_ring_buffer { static aas_ring_buffer of() { let result = new("aas_ring_buffer"); result.clear(); result._index = 0; return result; } void clear() { for (int i = 0; i < SIZE; ++i) { _ring[i] = 0; } } void write(int n) { _ring[_index] = n; _index = (_index + 1) % SIZE; } int get_sum() const { int result = 0; for (int i = 0; i < SIZE; ++i) { result += _ring[i]; } return result; } const SIZE = 20; private int[SIZE] _ring; private int _index; }
10.14.1. Test
class aas_mock_handler : aas_event_handler { static aas_mock_handler of() { let result = new("aas_mock_handler"); result._count = 0; return result; } override void on_event(int event_type) { ++_count; } int get_count() { return _count; } private int _count; } class aas_mock_counter : aas_enemies_counter { static aas_mock_counter of() { return new("aas_mock_counter"); } override int count() { return _count; } void set_count(int count) { _count = count; } private int _count; } class aas_active_enemies_checker_test : Clematis { override void testSuites() { describe("Testing active enemies checker"); let handler = aas_mock_handler.of(); let counter = aas_mock_counter.of(); let enemies_group_cvar = aas_cvar.of("aas_group_number"); let checker = aas_active_enemies_checker.of(handler, counter, enemies_group_cvar, aas_event.group_alert, aas_event.group_kill); for (int i = 0; i < 20; ++i) { counter.set_count(i * 10); checker.tick(); } it("must be only one event", AssertEval(handler.get_count(), "==", 1)); endDescribe(); } }
10.15. Clocks
10.15.1. Clock
// Interface for getting time. class aas_clock abstract { abstract int time(); }
10.15.2. Level clock
// Implements aas_clock interface by reading level time. class aas_level_clock : aas_clock { static aas_level_clock of() { return new("aas_level_clock"); } override int time() { return level.mapTime; } }
10.16. Counters
10.16.1. Enemies counter
// This is a generic interface for reporting how much enemies are on the level. class aas_enemies_counter abstract { abstract int count(); }
10.16.2. Active bosses counter
// This class implements aas_enemies_counter by counting active bosses. class aas_active_bosses_counter : aas_enemies_counter { static aas_active_bosses_counter of() { let result = new("aas_active_bosses_counter"); result._min_boss_health = aas_cvar.of("aas_min_boss_health"); return result; } override int count() { int result = 0; let i = ThinkerIterator.Create("Actor", Thinker.STAT_DEFAULT); Actor a; int min_boss_health = _min_boss_health.get_int(); while (a = Actor(i.Next())) { result += (is_active(a) && a.SpawnHealth() >= min_boss_health); } return result; } static bool is_active(Actor a) { return (a.bIsMonster && a.Target != NULL && a.Health > 0); } private aas_cvar _min_boss_health; }
10.16.3. Active enemies counter
// This class implements aas_enemies_counter by counting the active enemies. class aas_active_enemies_counter : aas_enemies_counter { static aas_active_enemies_counter of() { return new("aas_active_enemies_counter"); } override int count() { int result = 0; let i = ThinkerIterator.Create("Actor", Thinker.STAT_DEFAULT); Actor a; while (a = Actor(i.Next())) { result += is_active(a); } return result; } private static bool is_active(Actor a) { return (a.bIsMonster && a.Target != NULL && a.Health > 0); } }
10.17. Handlers
10.17.1. Event handler
// This is an interface for AAS event handlers. // // Note: This class is unrelated to EventHandler. class aas_event_handler abstract { // Make an action when event event_type happens. abstract void on_event(int event_type); }
10.17.2. Event handlers
// Implements aas_event_handler by storing and activating several event handlers. class aas_event_handlers : aas_event_handler { static aas_event_handlers of(array<aas_event_handler> handlers) { let result = new("aas_event_handlers"); result._handlers.move(handlers); return result; } override void on_event(int event_type) { foreach (handler : _handlers) handler.on_event(event_type); } private array<aas_event_handler> _handlers; }
10.17.3. Logger
class aas_logger : aas_event_handler { // Create aas_logger instance. // // log_function: a function that receives one parameter of type string. // level_cvar_name: a name of int CVar that controls verbosity level. static aas_logger of(aas_function log_function, string level_cvar_name) { let result = new("aas_logger"); PlayerInfo player = players[consolePlayer]; result._level = aas_cvar.of(level_cvar_name, player); result._color = aas_cvar.of("aas_print_color", player); result._log_function = log_function; return result; } override void on_event(int event_type) { if (is_worth_logging(event_type, _level.get_int())) { int color = _color.get_int() + 97; // 97 == 'a' string message = string.Format("\c%c%s", color, aas_event.message(event_type)); _log_function.f1(message); } } private static bool is_worth_logging(int event_type, int level) { return (event_type <= level); } private aas_cvar _level; private aas_cvar _color; private aas_function _log_function; } class aas_log_function : aas_function { static aas_log_function of() { return new("aas_log_function"); } override void f1(string s) { aas_log.print(s); } } class aas_print_function : aas_function { static aas_print_function of(Font aFont) { let result = new("aas_print_function"); result._font = aFont; return result; } override void f1(string s) { Console.midPrint(_font, s, true); } Font _font; }
10.17.4. Saver
class aas_saver : aas_event_handler { static aas_saver of(aas_game_actions game_actions, aas_clock clock, aas_timestamp timestamp) { let result = new("aas_saver"); result._game_actions = game_actions; result._clock = clock; result._last_save_time = timestamp; result._min_save_wait_s = aas_cvar.of("aas_min_save_wait"); result._is_enabled = aas_cvar.of("aas_enabled"); result._min_health = aas_cvar.of("aas_health_limit"); return result; } override void on_event(int event_type) { if (is_too_frequent(event_type)) return; if (is_save_enabled(event_type)) { _last_save_time.set_time(_clock.time()); _game_actions.save(); } } private bool is_too_frequent(int event_type) { // manual saving cannot be too frequent. if (event_type == aas_event.manual) return false; int current_time = _clock.time(); int time_from_last_save_s = (current_time - _last_save_time.get_time()) / TICRATE; return time_from_last_save_s < _min_save_wait_s.get_int(); } private bool is_save_enabled(int event_type) { if (event_type == aas_event.manual) return true; if (!_is_enabled.get_bool()) return false; int health = players[consolePlayer].Health; int min_health = _min_health.get_int(); if (health < min_health) return false; string toggle_name = aas_event.toggle_name(event_type); CVar variable = CVar.GetCVar(toggle_name); if (variable == NULL) { aas_log.error(string.Format("variable %s not found", toggle_name)); return false; } return variable.GetInt(); } private aas_game_actions _game_actions; private aas_clock _clock; private aas_timestamp _last_save_time; private aas_cvar _min_save_wait_s; private aas_cvar _is_enabled; private aas_cvar _min_health; }
10.17.5. Screenshot maker
// This class implements aas_event_handler by making a screenshots on events. class aas_screenshot_maker : aas_event_handler { static aas_screenshot_maker of(aas_game_actions game_actions) { let result = new("aas_screenshot_maker"); result._game_actions = game_actions; result._shot_on_manual = aas_cvar.of("aas_screenshot_on_manual"); result._is_enabled = aas_cvar.of("aas_enabled"); return result; } override void on_event(int event_type) { if (shot_enabled(event_type)) { _game_actions.take_screenshot(); } } private bool shot_enabled(int event_type) { if (event_type == aas_event.manual) return _shot_on_manual.get_int(); if (!_is_enabled.get_bool()) return false; string toggle_name = aas_event.screenshot_toggle_name(event_type); CVar variable = CVar.GetCVar(toggle_name); if (variable == NULL) { aas_log.error(string.format("variable %s not found", toggle_name)); return false; } return variable.GetInt(); } private aas_game_actions _game_actions; private aas_cvar _shot_on_manual; private aas_cvar _is_enabled; }
10.17.6. Voice
// Implements aas_event_handler by playing a sound when an event happens. class aas_voice : aas_event_handler { static aas_voice of() { let result = new("aas_voice"); result._timeout = 0; result._voice_volume = aas_cvar.of("aas_voice_volume", players[consolePlayer]); return result; } override void on_event(int event_type) { bool is_enabled_for_event = CVar.GetCVar(aas_event.voice_toggle_name(event_type)).GetInt(); if (is_enabled_for_event) { _queue.push(event_type); } } void tick() { // Should I play something now? if (_timeout == 0) { // Yes, but is there something to play? if (_queue.size() == 0) return; int event_type = _queue[0]; string voice_file = string.format("aas/voice%d", event_type); int volume = clamp(_voice_volume.get_int(), 1, 10); for (int i = 0; i < volume; ++i) { S_StartSound(voice_file, CHAN_BODY, CHANF_OVERLAP); } _queue.delete(0); _timeout = int(ceil(S_GetLength(voice_file) * TICRATE)); } else { // No, wait more. --_timeout; } } // Event queue stores event types. private array<int> _queue; private int _timeout; private aas_cvar _voice_volume; }
11. Project setup
version 4.14.3 #include "zscript/aas_active_enemies_checker.zs" #include "zscript/aas_clock.zs" #include "zscript/aas_counters.zs" #include "zscript/aas_cvar.zs" #include "zscript/aas_event_source.zs" #include "zscript/aas_event.zs" #include "zscript/aas_function.zs" #include "zscript/aas_game_action_scheduler.zs" #include "zscript/aas_game_actions.zs" #include "zscript/aas_handlers.zs" #include "zscript/aas_log.zs" #include "zscript/aas_main.zs" #include "zscript/aas_precache_sounds.zs" #include "zscript/aas_save_timer.zs" #include "zscript/aas_timestamp.zs" #include "zscript/aas_token.zs" #include "zscript/aas_universal_token_examples.zs" #include "zscript/aas_Fonts.zs" #include "zscript/aas_StringUtils.zs"
GameInfo
{
EventHandlers = "aas_main"
}
12. Tests
12.1. Setup
wait 2; map map01 wait 4; netevent test:aas_event_test wait 4; netevent test:aas_active_enemies_checker_test wait 6; quit