Compare commits
	
		
			2 Commits
		
	
	
		
			0bd2f4827b
			...
			0edb4b50d2
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 0edb4b50d2 | |||
| a5f837189b | 
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -7,5 +7,8 @@ seagull.pak
 | 
				
			|||||||
build/**
 | 
					build/**
 | 
				
			||||||
build_cache/**
 | 
					build_cache/**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# autogenerated
 | 
				
			||||||
 | 
					doc/**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**/__pycache__/**
 | 
					**/__pycache__/**
 | 
				
			||||||
**.pyc
 | 
					**.pyc
 | 
				
			||||||
							
								
								
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +1,6 @@
 | 
				
			|||||||
[submodule "ext/imsky/wordlists"]
 | 
					[submodule "ext/imsky/wordlists"]
 | 
				
			||||||
	path = ext/imsky/wordlists
 | 
						path = ext/imsky/wordlists
 | 
				
			||||||
	url = https://github.com/imsky/wordlists
 | 
						url = https://github.com/imsky/wordlists
 | 
				
			||||||
 | 
					[submodule "ext/jothepro/doxygen-awesome-css"]
 | 
				
			||||||
 | 
						path = ext/jothepro/doxygen-awesome-css
 | 
				
			||||||
 | 
						url = https://github.com/jothepro/doxygen-awesome-css.git
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,4 @@
 | 
				
			|||||||
app/**
 | 
					app/**
 | 
				
			||||||
ext/imsky/wordlists/**
 | 
					ext/imsky/wordlists/**
 | 
				
			||||||
 | 
					static/image/splash.png
 | 
				
			||||||
seagull.spec
 | 
					seagull.spec
 | 
				
			||||||
							
								
								
									
										16
									
								
								.woodpecker/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								.woodpecker/Dockerfile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					FROM python:3.13-alpine AS build-rust
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# install rust environment (for rant)
 | 
				
			||||||
 | 
					ENV RUSTUP_HOME=/usr/local/rustup CARGO_HOME=/usr/local/cargo PATH=/usr/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin RUST_VERSION=1.61.0
 | 
				
			||||||
 | 
					RUN --mount=type=cache,target=/var/cache/apk apk add --update-cache rustup gcc musl-dev rsync
 | 
				
			||||||
 | 
					RUN rustup-init -y --profile minimal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# install rant
 | 
				
			||||||
 | 
					RUN --mount=type=cache,target=/root/.cargo \
 | 
				
			||||||
 | 
					    --mount=type=cache,target=/usr/local/cargo/git/db \
 | 
				
			||||||
 | 
					    --mount=type=cache,target=/usr/local/cargo/registry \
 | 
				
			||||||
 | 
					    cargo install --color=never rant --version 4.0.0-alpha.33 --root /opt/rant --features cli
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FROM python:3.13-alpine AS buildenv
 | 
				
			||||||
 | 
					ENV RUSTUP_HOME=/usr/local/rustup CARGO_HOME=/usr/local/cargo PATH=/opt/rant/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin RUST_VERSION=1.61.0
 | 
				
			||||||
 | 
					COPY --from=build-rust /opt/rant /opt/rant
 | 
				
			||||||
							
								
								
									
										12
									
								
								.woodpecker/build-env.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								.woodpecker/build-env.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					when:
 | 
				
			||||||
 | 
					  - event: push
 | 
				
			||||||
 | 
					    branch: main
 | 
				
			||||||
 | 
					    path: .woodpecker/Dockerfile
 | 
				
			||||||
 | 
					steps:
 | 
				
			||||||
 | 
					  - name: build
 | 
				
			||||||
 | 
					    image: woodpeckerci/plugin-docker-buildx
 | 
				
			||||||
 | 
					    settings:
 | 
				
			||||||
 | 
					      repo: vcs.otl-hga.net/nicole/seagull-game
 | 
				
			||||||
 | 
					      registry: vcs.otl-hga.net
 | 
				
			||||||
 | 
					      dockerfile: .woodpecker/Dockerfile
 | 
				
			||||||
 | 
					      tag: buildenv
 | 
				
			||||||
							
								
								
									
										17
									
								
								.woodpecker/build.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								.woodpecker/build.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					when:
 | 
				
			||||||
 | 
					  - event: push
 | 
				
			||||||
 | 
					    branch: trunk
 | 
				
			||||||
 | 
					steps:
 | 
				
			||||||
 | 
					  - name: build linux
 | 
				
			||||||
 | 
					    image: vcs.otl-hga.net/nicole/seagull-game:buildenv
 | 
				
			||||||
 | 
					    commands:
 | 
				
			||||||
 | 
					      - mkdir build && cd build
 | 
				
			||||||
 | 
					      - cargo install rant --version 4.0.0-alpha.33 --features cli --root $(pwd)/opt/rant
 | 
				
			||||||
 | 
					      - mkdir -p app/rant
 | 
				
			||||||
 | 
					      - python ../render-wordlists.py -i ../ext/imsky/wordlists -o rant ./app/rant/wordlist.rant
 | 
				
			||||||
 | 
					      - rsync -r ../app .
 | 
				
			||||||
 | 
					      - pip install -r app/requirements.txt
 | 
				
			||||||
 | 
					      - pip install -r app/requirements-build-desktop.txt
 | 
				
			||||||
 | 
					      - pip install -r app/requirements-desktop.txt
 | 
				
			||||||
 | 
					      - pip install -r app/requirements-desktop-linux.txt
 | 
				
			||||||
 | 
					      - pyinstaller seagull.spec
 | 
				
			||||||
							
								
								
									
										72
									
								
								CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
				
			|||||||
 | 
					cmake_minimum_required(VERSION 4.1)
 | 
				
			||||||
 | 
					project(seagull)
 | 
				
			||||||
 | 
					set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					find_package(Python3 REQUIRED COMPONENTS Interpreter)
 | 
				
			||||||
 | 
					set(sys_python ${Python3_EXECUTABLE})
 | 
				
			||||||
 | 
					find_package(Cargo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					find_program(RSYNC NAMES rsync)
 | 
				
			||||||
 | 
					find_program(ZIP NAMES zip)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					file(GLOB_RECURSE pydepends RELATIVE "${CMAKE_SOURCE_DIR}"
 | 
				
			||||||
 | 
					     "${CMAKE_SOURCE_DIR}/app/*.py")
 | 
				
			||||||
 | 
					file(GLOB_RECURSE basepakdepends RELATIVE "${CMAKE_SOURCE_DIR}"
 | 
				
			||||||
 | 
					     "${CMAKE_SOURCE_DIR}/app/basepak/*")
 | 
				
			||||||
 | 
					file(GLOB_RECURSE pakargs RELATIVE "${CMAKE_SOURCE_DIR}/pak"
 | 
				
			||||||
 | 
					     "${CMAKE_SOURCE_DIR}/pak/*")
 | 
				
			||||||
 | 
					file(GLOB_RECURSE pakdepends RELATIVE "${CMAKE_SOURCE_DIR}"
 | 
				
			||||||
 | 
					     "${CMAKE_SOURCE_DIR}/pak/*")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/dist")
 | 
				
			||||||
 | 
					add_custom_target(pak ALL
 | 
				
			||||||
 | 
					                  DEPENDS ${pakdepends}
 | 
				
			||||||
 | 
					                  WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/pak"
 | 
				
			||||||
 | 
					                  COMMENT "Creating seagull.pak"
 | 
				
			||||||
 | 
					                  COMMAND ${ZIP} -7r "${CMAKE_CURRENT_BINARY_DIR}/dist/seagull.pak" .)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# wordlists
 | 
				
			||||||
 | 
					set(WORDLIST_SOURCE_PATH "${CMAKE_SOURCE_DIR}/ext/imsky/wordlists" CACHE PATH "Source location of wordlists to use.")
 | 
				
			||||||
 | 
					file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/basepak/rant")
 | 
				
			||||||
 | 
					add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/basepak/rant/wordlist.rant"
 | 
				
			||||||
 | 
					                   COMMENT "Rendering wordlists"
 | 
				
			||||||
 | 
					                   COMMAND "${sys_python}" "${CMAKE_SOURCE_DIR}/render-wordlists.py" -i "${WORDLIST_SOURCE_PATH}" -o rant "${CMAKE_CURRENT_BINARY_DIR}/basepak/rant/wordlist.rant")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# build rant
 | 
				
			||||||
 | 
					set(rant_path "${CMAKE_CURRENT_BINARY_DIR}/opt/rant")
 | 
				
			||||||
 | 
					add_custom_command(OUTPUT "${rant_path}"
 | 
				
			||||||
 | 
					                   COMMENT "Compiling rant"
 | 
				
			||||||
 | 
					                   COMMAND "${CARGO_EXECUTABLE}" install rant --version 4.0.0-alpha.33 --root "${rant_path}" --features cli)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# venv
 | 
				
			||||||
 | 
					set(venv_path "${CMAKE_CURRENT_BINARY_DIR}/pyvenv")
 | 
				
			||||||
 | 
					execute_process(COMMAND "${Python3_EXECUTABLE}" -m venv "${venv_path}" --upgrade-deps
 | 
				
			||||||
 | 
					                COMMAND_ERROR_IS_FATAL ANY)
 | 
				
			||||||
 | 
					set(ENV{VIRTUAL_ENV} "${venv_path}")
 | 
				
			||||||
 | 
					set(Python3_FIND_VIRTUALENV ONLY)
 | 
				
			||||||
 | 
					unset(Python3_EXECUTABLE)
 | 
				
			||||||
 | 
					# re-find python to use venv
 | 
				
			||||||
 | 
					find_package(Python3 REQUIRED COMPONENTS Interpreter)
 | 
				
			||||||
 | 
					if (sys_python STREQUAL Python3_EXECUTABLE)
 | 
				
			||||||
 | 
					    message(FATAL_ERROR "Still using Python3 after venv activation")
 | 
				
			||||||
 | 
					endif()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					execute_process(COMMAND "${Python3_EXECUTABLE}" -m pip install -r "${CMAKE_SOURCE_DIR}/app/requirements.txt")
 | 
				
			||||||
 | 
					execute_process(COMMAND "${Python3_EXECUTABLE}" -m pip install -r "${CMAKE_SOURCE_DIR}/app/requirements-desktop.txt")
 | 
				
			||||||
 | 
					execute_process(COMMAND "${Python3_EXECUTABLE}" -m pip install -r "${CMAKE_SOURCE_DIR}/app/requirements-build-desktop.txt")
 | 
				
			||||||
 | 
					execute_process(COMMAND "${Python3_EXECUTABLE}" -m pip install -r "${CMAKE_SOURCE_DIR}/app/requirements-desktop-linux.txt")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					find_program(PYINSTALLER NAMES pyinstaller HINTS "${venv_path}/bin" REQUIRED)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if (WIN32)
 | 
				
			||||||
 | 
					    set(binname "seagull.exe")
 | 
				
			||||||
 | 
					else()
 | 
				
			||||||
 | 
					    set(binname "seagull")
 | 
				
			||||||
 | 
					endif()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					configure_file("seagull.spec.in" "seagull.spec")
 | 
				
			||||||
 | 
					add_custom_command(OUTPUT "dist/${binname}"
 | 
				
			||||||
 | 
					                   DEPENDS "${rant_path}" "${CMAKE_CURRENT_BINARY_DIR}/basepak/rant/wordlist.rant" ${pydepends} ${basepakdepends}
 | 
				
			||||||
 | 
					                   COMMENT "Building ${binname}"
 | 
				
			||||||
 | 
					                   COMMAND ${PYINSTALLER} --distpath "${CMAKE_CURRENT_BINARY_DIR}/dist" --workpath "${CMAKE_CURRENT_BINARY_DIR}/pyinstaller" seagull.spec)
 | 
				
			||||||
 | 
					add_custom_target(desktop ALL DEPENDS "dist/${binname}" pak)
 | 
				
			||||||
							
								
								
									
										31
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								README.md
									
									
									
									
									
								
							@@ -1,3 +1,30 @@
 | 
				
			|||||||
# seagull-game
 | 
					# Seagull Game
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Seagull game built on AWS, designed to learn cloud stuff.
 | 
					A long time ago, the first versions of Seagull Game were a prototype, just enough of a workload
 | 
				
			||||||
 | 
					to serve as an example while on my AWS Solutions Architect apprenticeship. But with the economic
 | 
				
			||||||
 | 
					crash of 2025 came a loss of my job and a lot of excess free time, so I'm making this into a
 | 
				
			||||||
 | 
					real deal game that you can play, in an attempt to preserve my rapidly dwindling sanity.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Build
 | 
				
			||||||
 | 
					### Prerequisites
 | 
				
			||||||
 | 
					* Python (>= 3.11 oughta do it)
 | 
				
			||||||
 | 
					* TCL/Tk (for `tkinter` Python module, used by PyInstaller splash screen)
 | 
				
			||||||
 | 
					* A working Rust toolchain, incl. `cargo`, to build [Rant]
 | 
				
			||||||
 | 
					* rsync (or rclone in Windows)
 | 
				
			||||||
 | 
					* On Linux, `tar` and `zstd` are used to cache intermediate build steps into archives. `zip` is also used to generate `.pak` files.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Linux
 | 
				
			||||||
 | 
					Simply run `./build-desktop.sh`, followed by `./build-pak.sh` to build seagull.pak.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Windows
 | 
				
			||||||
 | 
					A script is in progess to automate this, but is not yet fully ready. Here's the broad strokes routine:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* Install [Python from python.org][cpython-win]. Non standard variants such as ActivePython are not supported.
 | 
				
			||||||
 | 
					* Install [rclone] and [Rust][rust-downloads]. For Rust, scroll down to the bottom of the first table: you probably want `x86_64-pc-windows-msvc`, as an MSI file.
 | 
				
			||||||
 | 
					* Create a folder called "build" wherever you checked out this repository, and open a PowerShell terminal there.
 | 
				
			||||||
 | 
					* `python -m venv pyvenv`, then `Scripts\activate.ps1`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Rant]: https://github.com/rant-lang/rant
 | 
				
			||||||
 | 
					[cpython-win]: https://www.python.org/downloads/windows/
 | 
				
			||||||
 | 
					[rclone]: https://rclone.org/downloads/
 | 
				
			||||||
 | 
					[rust-downloads]: https://forge.rust-lang.org/infra/other-installation-methods.html
 | 
				
			||||||
@@ -1,8 +1,16 @@
 | 
				
			|||||||
<?xml version="1.0" standalone="yes"?>
 | 
					<?xml version="1.0" standalone="yes"?>
 | 
				
			||||||
<xs:schema id="UpgradeRules" targetNamespace="seagull:rules/upgrades" xmlns="seagull:rules/upgrades" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" attributeFormDefault="unqualified" elementFormDefault="qualified">
 | 
					<xs:schema id="UpgradeRules" targetNamespace="seagull:rules/upgrades" xmlns="seagull:rules/upgrades" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" attributeFormDefault="unqualified" elementFormDefault="qualified">
 | 
				
			||||||
  <xs:element name="UpgradeRules" msdata:IsDataSet="true" msdata:UseCurrentLocale="true">
 | 
					  <xs:element name="UpgradeRules">
 | 
				
			||||||
    <xs:complexType>
 | 
					    <xs:complexType>
 | 
				
			||||||
      <xs:choice minOccurs="0" maxOccurs="unbounded">
 | 
					      <xs:choice minOccurs="0" maxOccurs="unbounded">
 | 
				
			||||||
 | 
					        <xs:element name="TreeData" maxOccurs="1">
 | 
				
			||||||
 | 
					          <xs:complexType>
 | 
				
			||||||
 | 
					            <xs:choice maxOccurs="unbounded">
 | 
				
			||||||
 | 
					              <xs:element name="Name" maxOccurs="1" type="xs:string" />
 | 
				
			||||||
 | 
					              <xs:element name="PrimaryColor" maxOccurs="1" type="xs:string" />
 | 
				
			||||||
 | 
					            </xs:choice>
 | 
				
			||||||
 | 
					          </xs:complexType>
 | 
				
			||||||
 | 
					        </xs:element>
 | 
				
			||||||
        <xs:element name="Upgrade">
 | 
					        <xs:element name="Upgrade">
 | 
				
			||||||
          <xs:complexType>
 | 
					          <xs:complexType>
 | 
				
			||||||
            <xs:choice maxOccurs="unbounded">
 | 
					            <xs:choice maxOccurs="unbounded">
 | 
				
			||||||
@@ -23,6 +31,13 @@
 | 
				
			|||||||
                  </xs:sequence>
 | 
					                  </xs:sequence>
 | 
				
			||||||
                </xs:complexType>
 | 
					                </xs:complexType>
 | 
				
			||||||
              </xs:element>
 | 
					              </xs:element>
 | 
				
			||||||
 | 
					              <xs:element name="Requirements" maxOccurs="1" minOccurs="0">
 | 
				
			||||||
 | 
					                <xs:complexType>
 | 
				
			||||||
 | 
					                  <xs:sequence>
 | 
				
			||||||
 | 
					                    <xs:element name="Require" maxOccurs="unbounded" type="xs:string" />
 | 
				
			||||||
 | 
					                  </xs:sequence>
 | 
				
			||||||
 | 
					                </xs:complexType>
 | 
				
			||||||
 | 
					              </xs:element>
 | 
				
			||||||
            </xs:choice>
 | 
					            </xs:choice>
 | 
				
			||||||
          </xs:complexType>
 | 
					          </xs:complexType>
 | 
				
			||||||
        </xs:element>
 | 
					        </xs:element>
 | 
				
			||||||
							
								
								
									
										164
									
								
								app/basepak/static/js/konami.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								app/basepak/static/js/konami.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,164 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Konami-JS ~
 | 
				
			||||||
 | 
					 * :: Now with support for touch events and multiple instances for
 | 
				
			||||||
 | 
					 * :: those situations that call for multiple easter eggs!
 | 
				
			||||||
 | 
					 * Code: https://github.com/georgemandis/konami-js
 | 
				
			||||||
 | 
					 * Copyright (c) 2009 George Mandis (https://george.mand.is)
 | 
				
			||||||
 | 
					 * Version: 1.7.0 (09/03/2024)
 | 
				
			||||||
 | 
					 * Licensed under the MIT License (http://opensource.org/licenses/MIT)
 | 
				
			||||||
 | 
					 * Tested in: Safari 4+, Google Chrome 4+, Firefox 3+, IE7+, Mobile Safari 2.2.1+ and Android
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var Konami = function (callback) {
 | 
				
			||||||
 | 
					  var konami = {
 | 
				
			||||||
 | 
					    addEvent: function (obj, type, fn, ref_obj) {
 | 
				
			||||||
 | 
					      if (obj.addEventListener) obj.addEventListener(type, fn, false);
 | 
				
			||||||
 | 
					      else if (obj.attachEvent) {
 | 
				
			||||||
 | 
					        // IE
 | 
				
			||||||
 | 
					        obj["e" + type + fn] = fn;
 | 
				
			||||||
 | 
					        obj[type + fn] = function () {
 | 
				
			||||||
 | 
					          obj["e" + type + fn](window.event, ref_obj);
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        obj.attachEvent("on" + type, obj[type + fn]);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    removeEvent: function (obj, eventName, eventCallback) {
 | 
				
			||||||
 | 
					      if (obj.removeEventListener) {
 | 
				
			||||||
 | 
					        obj.removeEventListener(eventName, eventCallback);
 | 
				
			||||||
 | 
					      } else if (obj.attachEvent) {
 | 
				
			||||||
 | 
					        obj.detachEvent(eventName);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    input: "",
 | 
				
			||||||
 | 
					    pattern: "38384040373937396665",
 | 
				
			||||||
 | 
					    keydownHandler: function (e, ref_obj) {
 | 
				
			||||||
 | 
					      if (ref_obj) {
 | 
				
			||||||
 | 
					        konami = ref_obj;
 | 
				
			||||||
 | 
					      } // IE
 | 
				
			||||||
 | 
					      konami.input += e ? e.keyCode : event.keyCode;
 | 
				
			||||||
 | 
					      if (konami.input.length > konami.pattern.length) {
 | 
				
			||||||
 | 
					        konami.input = konami.input.substr(
 | 
				
			||||||
 | 
					          konami.input.length - konami.pattern.length,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (konami.input === konami.pattern) {
 | 
				
			||||||
 | 
					        konami.code(konami._currentLink);
 | 
				
			||||||
 | 
					        konami.input = "";
 | 
				
			||||||
 | 
					        e.preventDefault();
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    load: function (link) {
 | 
				
			||||||
 | 
					      this._currentLink = link;
 | 
				
			||||||
 | 
					      this.addEvent(document, "keydown", this.keydownHandler, this);
 | 
				
			||||||
 | 
					      this.iphone.load(link);
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    unload: function () {
 | 
				
			||||||
 | 
					      this.removeEvent(document, "keydown", this.keydownHandler);
 | 
				
			||||||
 | 
					      this.iphone.unload();
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    code: function (link) {
 | 
				
			||||||
 | 
					      window.location = link;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    iphone: {
 | 
				
			||||||
 | 
					      start_x: 0,
 | 
				
			||||||
 | 
					      start_y: 0,
 | 
				
			||||||
 | 
					      stop_x: 0,
 | 
				
			||||||
 | 
					      stop_y: 0,
 | 
				
			||||||
 | 
					      tap: false,
 | 
				
			||||||
 | 
					      capture: false,
 | 
				
			||||||
 | 
					      orig_keys: "",
 | 
				
			||||||
 | 
					      keys: [
 | 
				
			||||||
 | 
					        "UP",
 | 
				
			||||||
 | 
					        "UP",
 | 
				
			||||||
 | 
					        "DOWN",
 | 
				
			||||||
 | 
					        "DOWN",
 | 
				
			||||||
 | 
					        "LEFT",
 | 
				
			||||||
 | 
					        "RIGHT",
 | 
				
			||||||
 | 
					        "LEFT",
 | 
				
			||||||
 | 
					        "RIGHT",
 | 
				
			||||||
 | 
					        "TAP",
 | 
				
			||||||
 | 
					        "TAP",
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      input: [],
 | 
				
			||||||
 | 
					      code: function (link) {
 | 
				
			||||||
 | 
					        konami.code(link);
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      touchmoveHandler: function (e) {
 | 
				
			||||||
 | 
					        if (e.touches.length === 1 && konami.iphone.capture === true) {
 | 
				
			||||||
 | 
					          var touch = e.touches[0];
 | 
				
			||||||
 | 
					          konami.iphone.stop_x = touch.pageX;
 | 
				
			||||||
 | 
					          konami.iphone.stop_y = touch.pageY;
 | 
				
			||||||
 | 
					          konami.iphone.tap = false;
 | 
				
			||||||
 | 
					          konami.iphone.capture = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      touchendHandler: function () {
 | 
				
			||||||
 | 
					        konami.iphone.input.push(konami.iphone.check_direction());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (konami.iphone.input.length > konami.iphone.keys.length)
 | 
				
			||||||
 | 
					          konami.iphone.input.shift();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (konami.iphone.input.length === konami.iphone.keys.length) {
 | 
				
			||||||
 | 
					          var match = true;
 | 
				
			||||||
 | 
					          for (var i = 0; i < konami.iphone.keys.length; i++) {
 | 
				
			||||||
 | 
					            if (konami.iphone.input[i] !== konami.iphone.keys[i]) {
 | 
				
			||||||
 | 
					              match = false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          if (match) {
 | 
				
			||||||
 | 
					            konami.iphone.code(konami._currentLink);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      touchstartHandler: function (e) {
 | 
				
			||||||
 | 
					        konami.iphone.start_x = e.changedTouches[0].pageX;
 | 
				
			||||||
 | 
					        konami.iphone.start_y = e.changedTouches[0].pageY;
 | 
				
			||||||
 | 
					        konami.iphone.tap = true;
 | 
				
			||||||
 | 
					        konami.iphone.capture = true;
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      load: function (link) {
 | 
				
			||||||
 | 
					        this.orig_keys = this.keys;
 | 
				
			||||||
 | 
					        konami.addEvent(document, "touchmove", this.touchmoveHandler);
 | 
				
			||||||
 | 
					        konami.addEvent(document, "touchend", this.touchendHandler, false);
 | 
				
			||||||
 | 
					        konami.addEvent(document, "touchstart", this.touchstartHandler);
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      unload: function () {
 | 
				
			||||||
 | 
					        konami.removeEvent(document, "touchmove", this.touchmoveHandler);
 | 
				
			||||||
 | 
					        konami.removeEvent(document, "touchend", this.touchendHandler);
 | 
				
			||||||
 | 
					        konami.removeEvent(document, "touchstart", this.touchstartHandler);
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      check_direction: function () {
 | 
				
			||||||
 | 
					        var x_magnitude = Math.abs(this.start_x - this.stop_x);
 | 
				
			||||||
 | 
					        var y_magnitude = Math.abs(this.start_y - this.stop_y);
 | 
				
			||||||
 | 
					        var x = this.start_x - this.stop_x < 0 ? "RIGHT" : "LEFT";
 | 
				
			||||||
 | 
					        var y = this.start_y - this.stop_y < 0 ? "DOWN" : "UP";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var result =
 | 
				
			||||||
 | 
					          this.tap === true ? "TAP" : x_magnitude > y_magnitude ? x : y;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  typeof callback === "string" && konami.load(callback);
 | 
				
			||||||
 | 
					  if (typeof callback === "function") {
 | 
				
			||||||
 | 
					    konami.code = callback;
 | 
				
			||||||
 | 
					    konami.load();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return konami;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if (typeof module !== "undefined" && typeof module.exports !== "undefined") {
 | 
				
			||||||
 | 
					  module.exports = Konami;
 | 
				
			||||||
 | 
					} else {
 | 
				
			||||||
 | 
					  if (typeof define === "function" && define.amd) {
 | 
				
			||||||
 | 
					    define([], function () {
 | 
				
			||||||
 | 
					      return Konami;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    window.Konami = Konami;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										14
									
								
								app/basepak/static/js/mermaid.esm.min.mjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								app/basepak/static/js/mermaid.esm.min.mjs
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -1,5 +1,8 @@
 | 
				
			|||||||
#!/usr/bin/env python
 | 
					#!/usr/bin/env python
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## \file desktop.py
 | 
				
			||||||
 | 
					#  \brief Entrypoint file for desktop versions of the Seagull Game.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import argparse
 | 
					import argparse
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
import pathlib
 | 
					import pathlib
 | 
				
			||||||
@@ -9,8 +12,19 @@ import webview
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import flask
 | 
					import flask
 | 
				
			||||||
import fs.tree
 | 
					import fs.tree
 | 
				
			||||||
 | 
					try:
 | 
				
			||||||
 | 
					    import pyi_splash
 | 
				
			||||||
 | 
					except ImportError:
 | 
				
			||||||
 | 
					    class NullModule():
 | 
				
			||||||
 | 
					        def update_text(self, *args, **kwargs):
 | 
				
			||||||
 | 
					            pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from pylocal import core, actions, desktop, dev, gamedata, items, tick
 | 
					        def close(self):
 | 
				
			||||||
 | 
					            pass
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    pyi_splash = NullModule()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from pylocal import core, actions, desktop, dev, gamedata, items, tick, upgrades
 | 
				
			||||||
 | 
					
 | 
				
			||||||
core.desktop_mode = True
 | 
					core.desktop_mode = True
 | 
				
			||||||
sig_exit = threading.Event()
 | 
					sig_exit = threading.Event()
 | 
				
			||||||
@@ -23,13 +37,14 @@ argo = argp.parse_args()
 | 
				
			|||||||
def index():
 | 
					def index():
 | 
				
			||||||
    if not core.base_context_live:
 | 
					    if not core.base_context_live:
 | 
				
			||||||
        core.render_base_context()
 | 
					        core.render_base_context()
 | 
				
			||||||
        core.base_context["scripts"].insert(0, core.app.url_for("static", filename="js/desktop-structuredclone.js"))
 | 
					        core.base_context["scripts"].insert(0, (core.app.url_for("static", filename="js/desktop-structuredclone.js"), False))
 | 
				
			||||||
        core.base_context["scripts"].insert(1, core.app.url_for("static", filename="js/seagull-desktop.js"))
 | 
					        core.base_context["scripts"].insert(1, (core.app.url_for("static", filename="js/seagull-desktop.js"), False))
 | 
				
			||||||
    gamedata.vfs.copy_out("templates/main_page.j2", dest=core.path_appdir.as_posix())
 | 
					    gamedata.vfs.copy_out("templates/main_page.j2", dest=core.path_appdir.as_posix())
 | 
				
			||||||
    return flask.render_template("main_page.j2", **core.base_context)
 | 
					    return flask.render_template("main_page.j2", **core.base_context)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == "__main__":
 | 
					if __name__ == "__main__":
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
 | 
					        pyi_splash.update_text("Determining storage directory")
 | 
				
			||||||
        if sys.platform.startswith("win"):
 | 
					        if sys.platform.startswith("win"):
 | 
				
			||||||
            storage_dir = pathlib.Path(os.environ["APPDATA"]) / "seagull"
 | 
					            storage_dir = pathlib.Path(os.environ["APPDATA"]) / "seagull"
 | 
				
			||||||
        elif sys.platform.startswith("darwin"): # macos
 | 
					        elif sys.platform.startswith("darwin"): # macos
 | 
				
			||||||
@@ -39,13 +54,16 @@ if __name__ == "__main__":
 | 
				
			|||||||
            storage_dir = pathlib.Path(os.environ["HOME"]) / ".local/share/seagull"
 | 
					            storage_dir = pathlib.Path(os.environ["HOME"]) / ".local/share/seagull"
 | 
				
			||||||
        desktop.path_storagedir = storage_dir
 | 
					        desktop.path_storagedir = storage_dir
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        pyi_splash.update_text("Loading VFS")
 | 
				
			||||||
        gamedata.vfs.load_data_source("basepak")
 | 
					        gamedata.vfs.load_data_source("basepak")
 | 
				
			||||||
        gamedata.vfs.load_data_source("seagull.pak", proto="zip")
 | 
					        gamedata.vfs.load_data_source("seagull.pak", proto="zip")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if argo.debug:
 | 
					        if argo.debug:
 | 
				
			||||||
            desktop.api.debug_mode = True
 | 
					            desktop.api.debug_mode = True
 | 
				
			||||||
 | 
					        pyi_splash.update_text("Starting browser shell")
 | 
				
			||||||
        storage_dir.mkdir(exist_ok=True, parents=True)
 | 
					        storage_dir.mkdir(exist_ok=True, parents=True)
 | 
				
			||||||
        webview.create_window("Seagull Game", core.app, js_api=desktop.api)
 | 
					        webview.create_window("Seagull Game", core.app, js_api=desktop.api, width=1280, height=720)
 | 
				
			||||||
 | 
					        pyi_splash.close()
 | 
				
			||||||
        webview.start(private_mode=False, storage_path=storage_dir.as_posix(), debug=True if argo.debug else False)
 | 
					        webview.start(private_mode=False, storage_path=storage_dir.as_posix(), debug=True if argo.debug else False)
 | 
				
			||||||
    except KeyboardInterrupt:
 | 
					    except KeyboardInterrupt:
 | 
				
			||||||
        core.log.info("Goodnight, moon ...")
 | 
					        core.log.info("Goodnight, moon ...")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,15 @@ import flask
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from . import core
 | 
					from . import core
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## \brief Rolls a random integer between `min` and `max`, and applies all described `modifiers`.
 | 
				
			||||||
 | 
					#  \param min The lowest possible number. Default is 0.
 | 
				
			||||||
 | 
					#  \param max The highest possible number. Default is 100.
 | 
				
			||||||
 | 
					#  \param modifiers
 | 
				
			||||||
 | 
					#  \return The result of the dice roll, after modifiers.
 | 
				
			||||||
 | 
					#  
 | 
				
			||||||
 | 
					#  Dice roll is actually a slight misnomer; the game primarily uses percentages for chance
 | 
				
			||||||
 | 
					#  calculations. However, the recognized term for these chance calculations, "dice rolls",
 | 
				
			||||||
 | 
					#  is D&D old, so is used here for mental ease.
 | 
				
			||||||
def dice_roll(min=0, max=100, modifiers=[]):
 | 
					def dice_roll(min=0, max=100, modifiers=[]):
 | 
				
			||||||
    result = random.randint(min, max)
 | 
					    result = random.randint(min, max)
 | 
				
			||||||
    for _, mod in modifiers:
 | 
					    for _, mod in modifiers:
 | 
				
			||||||
@@ -12,12 +21,29 @@ def dice_roll(min=0, max=100, modifiers=[]):
 | 
				
			|||||||
    
 | 
					    
 | 
				
			||||||
    return result
 | 
					    return result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def calculate_speed(agl):
 | 
				
			||||||
 | 
					    return 3+(agl * 1.5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## \brief Attempts to steal a given resource from a target.
 | 
				
			||||||
 | 
					#  \param resource The resource to steal.
 | 
				
			||||||
 | 
					#  \param target The class of target to steal from.
 | 
				
			||||||
 | 
					#  \api{POST} /act/steal/`<resource>`/`<target>`
 | 
				
			||||||
 | 
					#  \apidata Gamestate.
 | 
				
			||||||
@core.app.route("/act/steal/<resource>/<target>", methods=["POST"])
 | 
					@core.app.route("/act/steal/<resource>/<target>", methods=["POST"])
 | 
				
			||||||
def steal_resource(resource, target):
 | 
					def steal_resource(resource, target):
 | 
				
			||||||
 | 
					    core.log.debug(flask.request.get_data())
 | 
				
			||||||
 | 
					    gamestate = flask.request.get_json()["gamestate"]
 | 
				
			||||||
 | 
					    agl = gamestate["agility"]
 | 
				
			||||||
 | 
					    roll = dice_roll()
 | 
				
			||||||
 | 
					    speed = calculate_speed(agl)
 | 
				
			||||||
 | 
					    result = (roll + speed) + (agl/4)
 | 
				
			||||||
    return flask.Response(json.dumps({
 | 
					    return flask.Response(json.dumps({
 | 
				
			||||||
        "success": (dice_roll() >= 50)
 | 
					        "success": (result >= 50)
 | 
				
			||||||
    }), status=200, content_type="application/json")
 | 
					    }), status=200, content_type="application/json")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## \brief Attempts to recruit a seagull.
 | 
				
			||||||
 | 
					#  \api{POST} /act/recruit
 | 
				
			||||||
 | 
					#  \apidata Gamestate.
 | 
				
			||||||
@core.app.route("/act/recruit", methods=["POST"])
 | 
					@core.app.route("/act/recruit", methods=["POST"])
 | 
				
			||||||
def recruit():
 | 
					def recruit():
 | 
				
			||||||
    return flask.Response(json.dumps({
 | 
					    return flask.Response(json.dumps({
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,7 @@ import pathlib
 | 
				
			|||||||
import sys
 | 
					import sys
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import flask
 | 
					import flask
 | 
				
			||||||
 | 
					from flask_cors import CORS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
log = logging.getLogger()
 | 
					log = logging.getLogger()
 | 
				
			||||||
pipe_stderr = logging.StreamHandler(sys.stderr)
 | 
					pipe_stderr = logging.StreamHandler(sys.stderr)
 | 
				
			||||||
@@ -16,11 +17,16 @@ else:
 | 
				
			|||||||
    path_appdir = pathlib.Path.cwd()
 | 
					    path_appdir = pathlib.Path.cwd()
 | 
				
			||||||
log.critical(path_appdir)
 | 
					log.critical(path_appdir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## \internal
 | 
				
			||||||
 | 
					#  \brief Signals whether we are a desktop application (as opposed to a Docker container).
 | 
				
			||||||
desktop_mode = False
 | 
					desktop_mode = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from . import gamedata
 | 
					from . import gamedata
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## \internal
 | 
				
			||||||
 | 
					#  \brief The Flask instance. See <a href="https://flask.palletsprojects.com/en/stable/api/">Flask documentation</a>.
 | 
				
			||||||
app = flask.Flask("seagull-game", root_path=path_appdir, template_folder="templates", static_folder="static")
 | 
					app = flask.Flask("seagull-game", root_path=path_appdir, template_folder="templates", static_folder="static")
 | 
				
			||||||
 | 
					CORS(app)
 | 
				
			||||||
orig_url_for = app.url_for
 | 
					orig_url_for = app.url_for
 | 
				
			||||||
 | 
					
 | 
				
			||||||
xml_namespaces = {
 | 
					xml_namespaces = {
 | 
				
			||||||
@@ -34,6 +40,10 @@ xml_namespaces = {
 | 
				
			|||||||
#REDIS_PASS="i am not a real password"
 | 
					#REDIS_PASS="i am not a real password"
 | 
				
			||||||
#state_cache = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, username=REDIS_USER, password=REDIS_PASS)
 | 
					#state_cache = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, username=REDIS_USER, password=REDIS_PASS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# ham5 standing by...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## \internal
 | 
				
			||||||
 | 
					#  \brief Returns CDN URLs for files not present within the container itself, or copies VFS data out so Flask etc can use it.
 | 
				
			||||||
def url_for_override(endpoint, *posargs, _anchor=None, _method=None, _scheme=None, _external=None, self=app, **values):
 | 
					def url_for_override(endpoint, *posargs, _anchor=None, _method=None, _scheme=None, _external=None, self=app, **values):
 | 
				
			||||||
    if endpoint == "static":
 | 
					    if endpoint == "static":
 | 
				
			||||||
        if not gamedata.vfs.exists(f"static/{values["filename"]}"):
 | 
					        if not gamedata.vfs.exists(f"static/{values["filename"]}"):
 | 
				
			||||||
@@ -48,17 +58,25 @@ def url_for_override(endpoint, *posargs, _anchor=None, _method=None, _scheme=Non
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
app.url_for = url_for_override
 | 
					app.url_for = url_for_override
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## \internal
 | 
				
			||||||
 | 
					#  \brief Base Flask rendering context. Generated with render_base_context().
 | 
				
			||||||
base_context = {}
 | 
					base_context = {}
 | 
				
			||||||
base_context_live = False
 | 
					base_context_live = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## \brief Renders a dialog template and sends it to the client.
 | 
				
			||||||
 | 
					#  \param dialog The dialog to render.
 | 
				
			||||||
 | 
					#  \api{GET} /dialog/`<dialog>`
 | 
				
			||||||
@app.route("/dialog/<dialog>")
 | 
					@app.route("/dialog/<dialog>")
 | 
				
			||||||
def render_dialog(dialog):
 | 
					def render_dialog(dialog):
 | 
				
			||||||
    if gamedata.vfs.exists(f"templates/{dialog}.j2"):
 | 
					    if gamedata.vfs.exists(f"templates/{dialog}.j2"):
 | 
				
			||||||
        gamedata.vfs.copy_out(f"templates/{dialog}.j2", dest=path_appdir.as_posix())
 | 
					        gamedata.vfs.copy_out(f"templates/{dialog}.j2", dest=path_appdir.as_posix())
 | 
				
			||||||
 | 
					        if gamedata.vfs.exists(f"static/js/dlg-{dialog}.js"):
 | 
				
			||||||
 | 
					            gamedata.vfs.copy_out(f"static/js/dlg-{dialog}.js", dest=path_appdir.as_posix())
 | 
				
			||||||
        return flask.render_template(f"{dialog}.j2")
 | 
					        return flask.render_template(f"{dialog}.j2")
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
        return "", 404
 | 
					        return "", 404
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## \brief Prepares the base rendering context for Flask to serve our content.
 | 
				
			||||||
def render_base_context():
 | 
					def render_base_context():
 | 
				
			||||||
    global base_context
 | 
					    global base_context
 | 
				
			||||||
    global base_context_live
 | 
					    global base_context_live
 | 
				
			||||||
@@ -68,19 +86,32 @@ def render_base_context():
 | 
				
			|||||||
    domain_components = flask.request.host.split(".")
 | 
					    domain_components = flask.request.host.split(".")
 | 
				
			||||||
    base_domain = ".".join(domain_components[-2:])
 | 
					    base_domain = ".".join(domain_components[-2:])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    gamedata.vfs.copy_out("static/js/mermaid.esm.min.mjs", dest=path_appdir.as_posix())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # all this wind up for...
 | 
					    # all this wind up for...
 | 
				
			||||||
    if base_domain == "otl-hga.net": # production, use assets from S3
 | 
					    if base_domain == "otl-hga.net": # production, use assets from S3
 | 
				
			||||||
        base_context["styles"] = ["https://cdn.otl-hga.net/seagull/css/seagull.css"]
 | 
					        base_context["styles"] = ["https://cdn.otl-hga.net/seagull/css/seagull.css"]
 | 
				
			||||||
        base_context["scripts"] = ["https://cdn.otl-hga.net/seagull/js/seagull.js"]
 | 
					        base_context["scripts"] = ["https://cdn.otl-hga.net/seagull/js/seagull.js", "https://cdn.otl-hga.net/seagull/js/konami.js"]
 | 
				
			||||||
        base_context["seagull_pic"] = "https://cdn.otl-hga.net/seagull/image/seagull.jpg"
 | 
					        base_context["seagull_pic"] = "https://cdn.otl-hga.net/seagull/image/seagull.jpg"
 | 
				
			||||||
    else:                            # dev, serve files from here
 | 
					    else:                            # dev, serve files from here
 | 
				
			||||||
        #print(base_domain)
 | 
					        #print(base_domain)
 | 
				
			||||||
        base_context["styles"] = [app.url_for("static", filename="css/seagull.css")]
 | 
					        base_context["styles"] = [app.url_for("static", filename="css/seagull.css")]
 | 
				
			||||||
        base_context["scripts"] = [app.url_for("static", filename="js/seagull.js")]
 | 
					        base_context["scripts"] = [(app.url_for("static", filename="js/konami.js"), True), (app.url_for("static", filename="js/seagull.js"), True)]
 | 
				
			||||||
        base_context["seagull_pic"] = app.url_for("static", filename="image/seagull.jpg")
 | 
					        base_context["seagull_pic"] = app.url_for("static", filename="image/seagull.jpg")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    base_context_live = True
 | 
					    base_context_live = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## \brief Returns OK. Useful for health checks.
 | 
				
			||||||
 | 
					#  \api{GET} /core/ping
 | 
				
			||||||
@app.route("/core/ping")
 | 
					@app.route("/core/ping")
 | 
				
			||||||
def healthcheck_ping():
 | 
					def healthcheck_ping():
 | 
				
			||||||
 | 
					    return flask.Response("OK", content_type="text/plain")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## \brief Informs the game we're about to request a file from JavaScript.
 | 
				
			||||||
 | 
					#  \internal
 | 
				
			||||||
 | 
					#  \api{POST} /core/ready_file
 | 
				
			||||||
 | 
					#  \apidata Plaintext path to the intended file.
 | 
				
			||||||
 | 
					@app.route("/core/ready_file", methods=["POST"])
 | 
				
			||||||
 | 
					def ready_file():
 | 
				
			||||||
 | 
					    gamedata.vfs.copy_out(flask.request.data)
 | 
				
			||||||
    return flask.Response("OK", content_type="text/plain")
 | 
					    return flask.Response("OK", content_type="text/plain")
 | 
				
			||||||
@@ -2,14 +2,29 @@ import pathlib
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from . import core
 | 
					from . import core
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## \brief A <a href="https://docs.python.org/3/library/pathlib.html#pathlib.Path">standard Path object</a> pointing to where the user's local storage is.
 | 
				
			||||||
 | 
					#  
 | 
				
			||||||
 | 
					#  Exactly where this is depends on your operating system:
 | 
				
			||||||
 | 
					#  - On Windows, this is defined as `%%APPDATA%\seagull`, which typically evaluates to something like `"C:\Users\YourNameHere\AppData\Roaming\seagull"`
 | 
				
			||||||
 | 
					#  - On MacOS, this is defined as `~/Library/Application Support/seagull`, where ~ is your home directory.
 | 
				
			||||||
 | 
					#  - On Linux, this is defined as `~/.local/share/seagull`.
 | 
				
			||||||
 | 
					#  \note On Linux, the XDG specification for defining alternative data directories is not currently respected. This will likely change in a future version.
 | 
				
			||||||
path_storagedir = pathlib.Path()
 | 
					path_storagedir = pathlib.Path()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## \internal
 | 
				
			||||||
 | 
					#  \brief Defines JavaScript API functions for pywebview.
 | 
				
			||||||
class JS_API:
 | 
					class JS_API:
 | 
				
			||||||
 | 
					    #  \brief Whether or not we're in debug mode.
 | 
				
			||||||
    debug_mode = False
 | 
					    debug_mode = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, debug_mode=False):
 | 
					    def __init__(self, debug_mode=False):
 | 
				
			||||||
        self.debug_mode = debug_mode
 | 
					        self.debug_mode = debug_mode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ## \brief Loads data stored under a specific "key" in the local filesystem.
 | 
				
			||||||
 | 
					    #  \param key The key to load.
 | 
				
			||||||
 | 
					    #  \return File contents, or `None`.
 | 
				
			||||||
 | 
					    #  In the current implementation, key is just a raw filename loaded relative to path_storagedir,
 | 
				
			||||||
 | 
					    #  which is calculated as part of main thread startup.
 | 
				
			||||||
    def load_data(self, key):
 | 
					    def load_data(self, key):
 | 
				
			||||||
        if not (path_storagedir / key).exists():
 | 
					        if not (path_storagedir / key).exists():
 | 
				
			||||||
            return None
 | 
					            return None
 | 
				
			||||||
@@ -21,6 +36,11 @@ class JS_API:
 | 
				
			|||||||
                core.log.error(f"problem loading {key} (from {path_storagedir}): {exc}")
 | 
					                core.log.error(f"problem loading {key} (from {path_storagedir}): {exc}")
 | 
				
			||||||
                return None
 | 
					                return None
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    ## \brief Saves data stored under a specific "key" in the local filesystem.
 | 
				
			||||||
 | 
					    #  \param key The key to save to.
 | 
				
			||||||
 | 
					    #  \param data The data to write.
 | 
				
			||||||
 | 
					    #  In the current implementation, key is just a raw filename loaded relative to path_storagedir,
 | 
				
			||||||
 | 
					    #  which is calculated as part of main thread startup.
 | 
				
			||||||
    def save_data(self, key, data):
 | 
					    def save_data(self, key, data):
 | 
				
			||||||
        with open(path_storagedir / key, "w") as fd_datafile:
 | 
					        with open(path_storagedir / key, "w") as fd_datafile:
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
@@ -28,6 +48,8 @@ class JS_API:
 | 
				
			|||||||
            except Exception as exc:
 | 
					            except Exception as exc:
 | 
				
			||||||
                core.log.error(f"problem saving {key} (to {path_storagedir}): {exc}")
 | 
					                core.log.error(f"problem saving {key} (to {path_storagedir}): {exc}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ## \brief Deletes all data stored at key.
 | 
				
			||||||
 | 
					    #  \param key The key to delete.
 | 
				
			||||||
    def delete_data(self, key):
 | 
					    def delete_data(self, key):
 | 
				
			||||||
        if (path_storagedir / key).exists():
 | 
					        if (path_storagedir / key).exists():
 | 
				
			||||||
            (path_storagedir / key).unlink()
 | 
					            (path_storagedir / key).unlink()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,11 @@
 | 
				
			|||||||
import flask
 | 
					import flask
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from . import core
 | 
					from . import core, gamedata
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## \brief Renders the dev toolbox for the client
 | 
				
			||||||
 | 
					#  \api{GET} /dev/get-toolbox
 | 
				
			||||||
@core.app.route("/dev/get-toolbox")
 | 
					@core.app.route("/dev/get-toolbox")
 | 
				
			||||||
def dev_toolbox():
 | 
					def dev_toolbox():
 | 
				
			||||||
 | 
					    gamedata.vfs.copy_out("static/sound/open_dev_toolkit.wav", dest=core.path_appdir.as_posix())
 | 
				
			||||||
 | 
					    gamedata.vfs.copy_out("templates/dev_toolbox.j2", dest=core.path_appdir.as_posix())
 | 
				
			||||||
    return flask.render_template("dev_toolbox.j2", ipaddr=flask.request.remote_addr, desktop=core.desktop_mode)
 | 
					    return flask.render_template("dev_toolbox.j2", ipaddr=flask.request.remote_addr, desktop=core.desktop_mode)
 | 
				
			||||||
@@ -12,6 +12,8 @@ import fs.osfs
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from . import core
 | 
					from . import core
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## \brief Handler for virtual filesystem.
 | 
				
			||||||
 | 
					#  \internal
 | 
				
			||||||
class GameVFSHandler(object):
 | 
					class GameVFSHandler(object):
 | 
				
			||||||
    vfs = None
 | 
					    vfs = None
 | 
				
			||||||
    log = logging.getLogger().getChild("vfs")
 | 
					    log = logging.getLogger().getChild("vfs")
 | 
				
			||||||
@@ -43,6 +45,10 @@ class GameVFSHandler(object):
 | 
				
			|||||||
        except:
 | 
					        except:
 | 
				
			||||||
            raise
 | 
					            raise
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    ## \brief Load files from a given data source.
 | 
				
			||||||
 | 
					    #  \param source The data source. This can be a pyfilesystem2 object, or any string or path-like
 | 
				
			||||||
 | 
					    #                object that can reasonably be interpreted as a directory or zip file.
 | 
				
			||||||
 | 
					    #  \param proto The <a href="https://pyfilesystem2.readthedocs.io/en/latest/builtin.html">PyFilesystem2 filesystem protocol</a> to use. Defaults to `"osfs"`, which loads directories.
 | 
				
			||||||
    def load_data_source(self, source: pathlib.Path | fs.base.FS | typing.Text, proto="osfs"):
 | 
					    def load_data_source(self, source: pathlib.Path | fs.base.FS | typing.Text, proto="osfs"):
 | 
				
			||||||
        print(f"data source: {source} ({proto})")
 | 
					        print(f"data source: {source} ({proto})")
 | 
				
			||||||
        assert self.vfs is not None
 | 
					        assert self.vfs is not None
 | 
				
			||||||
@@ -63,6 +69,10 @@ class GameVFSHandler(object):
 | 
				
			|||||||
            self.log.info(f"loading vfs source: {fs_source} (pyfilesystem2 handler)")
 | 
					            self.log.info(f"loading vfs source: {fs_source} (pyfilesystem2 handler)")
 | 
				
			||||||
            fs.copy.copy_fs(fs_source, self.vfs)
 | 
					            fs.copy.copy_fs(fs_source, self.vfs)
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    ## \brief Copies a file out of VFS into the real filesystem.
 | 
				
			||||||
 | 
					    #  \param filepath The source file path to copy out.
 | 
				
			||||||
 | 
					    #  \param dest The path to write the file to. Defaults to temporary directory.
 | 
				
			||||||
 | 
					    #  \return The full destination file path.
 | 
				
			||||||
    def copy_out(self, filepath, dest=None):
 | 
					    def copy_out(self, filepath, dest=None):
 | 
				
			||||||
        if not dest:
 | 
					        if not dest:
 | 
				
			||||||
            self.osfs_temp.makedirs(pathlib.Path(filepath).parent.as_posix(), recreate=True)
 | 
					            self.osfs_temp.makedirs(pathlib.Path(filepath).parent.as_posix(), recreate=True)
 | 
				
			||||||
@@ -76,4 +86,6 @@ class GameVFSHandler(object):
 | 
				
			|||||||
            fs.copy.copy_file(self.vfs, filepath, dest, filepath)
 | 
					            fs.copy.copy_file(self.vfs, filepath, dest, filepath)
 | 
				
			||||||
            return (pth_dest / filepath).as_posix()
 | 
					            return (pth_dest / filepath).as_posix()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## \brief Primary VFS handler.
 | 
				
			||||||
 | 
					#  \internal
 | 
				
			||||||
vfs = GameVFSHandler()
 | 
					vfs = GameVFSHandler()
 | 
				
			||||||
@@ -10,6 +10,8 @@ valid_resources = [
 | 
				
			|||||||
    "food", "shinies", "psi" # early game
 | 
					    "food", "shinies", "psi" # early game
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## \internal
 | 
				
			||||||
 | 
					#  \brief The environment variable map to run Rant with.
 | 
				
			||||||
rant_env = os.environ.copy()
 | 
					rant_env = os.environ.copy()
 | 
				
			||||||
rant_env["RANT_MODULES_PATH"] = (core.path_appdir / "basepak/rant").as_posix()
 | 
					rant_env["RANT_MODULES_PATH"] = (core.path_appdir / "basepak/rant").as_posix()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -18,6 +20,12 @@ doc_item_schema = xmltree.parse(pth_item_schema.as_posix())
 | 
				
			|||||||
item_schema = xmltree.XMLSchema(doc_item_schema)
 | 
					item_schema = xmltree.XMLSchema(doc_item_schema)
 | 
				
			||||||
item_schema_parser = xmltree.XMLParser(schema=item_schema)
 | 
					item_schema_parser = xmltree.XMLParser(schema=item_schema)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## \brief Generates an absolutely reasonable description of a given item that a target might have.
 | 
				
			||||||
 | 
					#  \param resource The resource to generate a description for.
 | 
				
			||||||
 | 
					#  \param target The poor soul carrying the item.
 | 
				
			||||||
 | 
					#  \return An absolutely reasonable description of an item.
 | 
				
			||||||
 | 
					#  
 | 
				
			||||||
 | 
					#  \include{doc,local} res/doc/python/items.generate_item_description.mdpart
 | 
				
			||||||
def generate_item_description(resource, target):
 | 
					def generate_item_description(resource, target):
 | 
				
			||||||
    if core.desktop_mode:
 | 
					    if core.desktop_mode:
 | 
				
			||||||
        rant_path = core.path_appdir / "opt/rant/bin/rant"
 | 
					        rant_path = core.path_appdir / "opt/rant/bin/rant"
 | 
				
			||||||
@@ -29,6 +37,13 @@ def generate_item_description(resource, target):
 | 
				
			|||||||
        core.log.warning("rant is throwing up:\n" + proc_rant.stderr.decode())
 | 
					        core.log.warning("rant is throwing up:\n" + proc_rant.stderr.decode())
 | 
				
			||||||
    return proc_rant.stdout.decode().strip()
 | 
					    return proc_rant.stdout.decode().strip()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## \brief Generates a list of items worth `min`-`max` `resource` that `target` would reasonably have.
 | 
				
			||||||
 | 
					#  \param resource The resource to generate a description for.
 | 
				
			||||||
 | 
					#  \param target The poor soul carrying the item.
 | 
				
			||||||
 | 
					#  \param min The lowest possible value, per item.
 | 
				
			||||||
 | 
					#  \param max The highest possible value, per item.
 | 
				
			||||||
 | 
					#  \param storybeat Inform the rule parsing engine where we are in the story.
 | 
				
			||||||
 | 
					#  \return A list of TickItem instances.
 | 
				
			||||||
def generate_item_list(resource, target, min, max, storybeat=0):
 | 
					def generate_item_list(resource, target, min, max, storybeat=0):
 | 
				
			||||||
    count = random.randint(min, max)
 | 
					    count = random.randint(min, max)
 | 
				
			||||||
    result = []
 | 
					    result = []
 | 
				
			||||||
@@ -43,12 +58,12 @@ def generate_item_list(resource, target, min, max, storybeat=0):
 | 
				
			|||||||
        maxdata = res_rule.xpath("./items:Max", namespaces=core.xml_namespaces)[0]
 | 
					        maxdata = res_rule.xpath("./items:Max", namespaces=core.xml_namespaces)[0]
 | 
				
			||||||
        resource_rules.append((res_rule, int(mindata.text), int(maxdata.text)))
 | 
					        resource_rules.append((res_rule, int(mindata.text), int(maxdata.text)))
 | 
				
			||||||
    for i in range(0, count):
 | 
					    for i in range(0, count):
 | 
				
			||||||
        core.log.warning("TODO: we don't know which rule this parses yet")
 | 
					        core.log.warning(f"{resource} vs humans: {resource_rules[0][1]} <-> {resource_rules[0][2]} (sb: {resource_rules[0][0].get("StoryBeat", "0")})")
 | 
				
			||||||
        core.log.warning(f"{resource} vs humans: {resource_rules[0]}")
 | 
					 | 
				
			||||||
        result.append(TickItem(resource, round(random.uniform(resource_rules[0][1], resource_rules[0][2]), 2), target))
 | 
					        result.append(TickItem(resource, round(random.uniform(resource_rules[0][1], resource_rules[0][2]), 2), target))
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    return result
 | 
					    return result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## \brief A tick item.
 | 
				
			||||||
class TickItem(object):
 | 
					class TickItem(object):
 | 
				
			||||||
    def __init__(self, resource, amount, target):
 | 
					    def __init__(self, resource, amount, target):
 | 
				
			||||||
        if resource not in valid_resources:
 | 
					        if resource not in valid_resources:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,8 @@ import json
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from . import items
 | 
					from . import items
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## \brief Extended JSON encoder object to handle seagull-specific items.
 | 
				
			||||||
 | 
					#  \internal
 | 
				
			||||||
class JSONizer(json.JSONEncoder):
 | 
					class JSONizer(json.JSONEncoder):
 | 
				
			||||||
    def default(self, obj):
 | 
					    def default(self, obj):
 | 
				
			||||||
        if isinstance(obj, items.TickItem):
 | 
					        if isinstance(obj, items.TickItem):
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,7 @@ import subprocess
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import flask
 | 
					import flask
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from . import core, items, jsonizer
 | 
					from . import core, gamedata, items, jsonizer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
rant_env = os.environ.copy()
 | 
					rant_env = os.environ.copy()
 | 
				
			||||||
rant_env["RANT_MODULES_PATH"] = (core.path_appdir / "basepak/rant").as_posix()
 | 
					rant_env["RANT_MODULES_PATH"] = (core.path_appdir / "basepak/rant").as_posix()
 | 
				
			||||||
@@ -15,7 +15,8 @@ def generate_flavor_text():
 | 
				
			|||||||
        rant_path = core.path_appdir / "opt/rant/bin/rant"
 | 
					        rant_path = core.path_appdir / "opt/rant/bin/rant"
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
        rant_path = "rant" # rely on OS PATH
 | 
					        rant_path = "rant" # rely on OS PATH
 | 
				
			||||||
    proc_rant = subprocess.run([rant_path, (core.path_appdir / "basepak/rant/flavor.rant").as_posix()], env=rant_env, capture_output=True)
 | 
					    flavor_file = gamedata.vfs.copy_out("rant/flavor.rant")
 | 
				
			||||||
 | 
					    proc_rant = subprocess.run([rant_path, flavor_file], env=rant_env, capture_output=True)
 | 
				
			||||||
    return proc_rant.stdout.decode()
 | 
					    return proc_rant.stdout.decode()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TickEvent(object):
 | 
					class TickEvent(object):
 | 
				
			||||||
@@ -30,6 +31,9 @@ tick_event_list.append(TickEvent(1, 1, "FLAVOR"))       # procedurally generated
 | 
				
			|||||||
tick_event_list.append(TickEvent(10, 2, "ENCHUMAN"))    # encounter: human
 | 
					tick_event_list.append(TickEvent(10, 2, "ENCHUMAN"))    # encounter: human
 | 
				
			||||||
tick_event_list.append(TickEvent(11, 2, "ENCGULL"))
 | 
					tick_event_list.append(TickEvent(11, 2, "ENCGULL"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## \brief Returns the results of a game tick.
 | 
				
			||||||
 | 
					#  \api{GET} /tick
 | 
				
			||||||
 | 
					#  \return Game tick events and results.
 | 
				
			||||||
@core.app.route("/tick")
 | 
					@core.app.route("/tick")
 | 
				
			||||||
def tick():
 | 
					def tick():
 | 
				
			||||||
    #return random.choices([json.dumps({"code": 200, "event_type": 0}), json.dumps({"code": 200, "event_type": 1, "log": generate_flavor_text()})], weights=[16, 1])[0]
 | 
					    #return random.choices([json.dumps({"code": 200, "event_type": 0}), json.dumps({"code": 200, "event_type": 1, "log": generate_flavor_text()})], weights=[16, 1])[0]
 | 
				
			||||||
@@ -67,10 +71,28 @@ def tick():
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    return flask.Response(json.dumps(result, cls=jsonizer.JSONizer), status=200, content_type="application/json")
 | 
					    return flask.Response(json.dumps(result, cls=jsonizer.JSONizer), status=200, content_type="application/json")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## \brief Returns the results of a colony tick.
 | 
				
			||||||
 | 
					#  \api{GET} /tick/colony
 | 
				
			||||||
 | 
					#  \return The result of a colony tick.
 | 
				
			||||||
@core.app.route("/tick/colony", methods=["POST"])
 | 
					@core.app.route("/tick/colony", methods=["POST"])
 | 
				
			||||||
def tick_colony() -> flask.Response:
 | 
					def tick_colony() -> flask.Response:
 | 
				
			||||||
    req = flask.request.json
 | 
					    req = flask.request.json
 | 
				
			||||||
    if not req:
 | 
					    if not req:
 | 
				
			||||||
        return flask.make_response("Bad request", 400)
 | 
					        return flask.make_response("Bad request", 400)
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    colony = req["colony"]
 | 
				
			||||||
 | 
					    modifiers = req["modifiers"]
 | 
				
			||||||
 | 
					    inc_food = req["avg_food"]
 | 
				
			||||||
 | 
					    inc_shinies = req["avg_shinies"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    out_food = (inc_food * 0.35) * (colony / 2)
 | 
				
			||||||
 | 
					    out_shinies = (inc_shinies * 0.25) * (colony / 3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    result = {
 | 
				
			||||||
 | 
					        "success": True,
 | 
				
			||||||
 | 
					        "food": out_food,
 | 
				
			||||||
 | 
					        "shinies": out_shinies
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return flask.Response(json.dumps(result, cls=jsonizer.JSONizer), status=200, content_type="application/json")
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
							
								
								
									
										93
									
								
								app/pylocal/upgrades.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								app/pylocal/upgrades.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,93 @@
 | 
				
			|||||||
 | 
					import io
 | 
				
			||||||
 | 
					import logging
 | 
				
			||||||
 | 
					import textwrap
 | 
				
			||||||
 | 
					from collections import namedtuple
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import flask
 | 
				
			||||||
 | 
					import lxml.etree as xmltree
 | 
				
			||||||
 | 
					import mermaid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from . import core, gamedata
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pth_upgrade_schema = core.path_appdir / "basepak/rules/schemas/upgrades.xsd"
 | 
				
			||||||
 | 
					doc_upgrade_schema = xmltree.parse(pth_upgrade_schema.as_posix())
 | 
				
			||||||
 | 
					upgrade_schema = xmltree.XMLSchema(doc_upgrade_schema)
 | 
				
			||||||
 | 
					upgrade_schema_parser = xmltree.XMLParser(schema=upgrade_schema)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					UpgradeData = namedtuple("UpgradeData", ["id", "name", "desc", "requires"])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## \brief Renders an upgrade tree.
 | 
				
			||||||
 | 
					#  \param tree The upgrade tree to retrieve.
 | 
				
			||||||
 | 
					#  \api{GET} /upgrades/`<tree>`
 | 
				
			||||||
 | 
					#  \return An SVG.
 | 
				
			||||||
 | 
					@core.app.route("/upgrades/<tree>")
 | 
				
			||||||
 | 
					def get_upgrade_tree(tree):
 | 
				
			||||||
 | 
					    buf_mmd = io.StringIO()
 | 
				
			||||||
 | 
					    rulefile = xmltree.parse(gamedata.vfs.open(f"/rules/upgrades/{tree}.xml"), upgrade_schema_parser)
 | 
				
			||||||
 | 
					    ruleset = rulefile.getroot()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    hnd_treedata = ruleset.xpath("./upgrades:TreeData", namespaces=core.xml_namespaces)[0]
 | 
				
			||||||
 | 
					    hnd_name = hnd_treedata.xpath("./upgrades:Name", namespaces=core.xml_namespaces)[0]
 | 
				
			||||||
 | 
					    hnd_primary_color = hnd_treedata.xpath("./upgrades:PrimaryColor", namespaces=core.xml_namespaces)[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    buf_mmd.write(textwrap.dedent(f"""
 | 
				
			||||||
 | 
					    ----
 | 
				
			||||||
 | 
					    title: {hnd_name.text}
 | 
				
			||||||
 | 
					    theme: base
 | 
				
			||||||
 | 
					    themeVariables:
 | 
				
			||||||
 | 
					        primaryColor: {hnd_primary_color.text}
 | 
				
			||||||
 | 
					    ----
 | 
				
			||||||
 | 
					    flowchart LR
 | 
				
			||||||
 | 
					    """))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    tree_upgrades = []
 | 
				
			||||||
 | 
					    for hnd_upgrade in ruleset.iter("{seagull:rules/upgrades}Upgrade"):
 | 
				
			||||||
 | 
					        hnd_id = hnd_upgrade.xpath("./upgrades:Id", namespaces=core.xml_namespaces)[0]
 | 
				
			||||||
 | 
					        hnd_upgrade_name = hnd_upgrade.xpath("./upgrades:Name", namespaces=core.xml_namespaces)[0]
 | 
				
			||||||
 | 
					        hnd_desc = hnd_upgrade.xpath("./upgrades:Desc", namespaces=core.xml_namespaces)[0]
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            hnd_requires = hnd_upgrade.xpath("./upgrades:Requirements", namespaces=core.xml_namespaces)[0]
 | 
				
			||||||
 | 
					            require_list = [elem.text for elem in hnd_requires.iter("{seagull:rules/upgrades}Require")]
 | 
				
			||||||
 | 
					        except IndexError:
 | 
				
			||||||
 | 
					            require_list = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        upgrade = UpgradeData(
 | 
				
			||||||
 | 
					            id=hnd_id.text,
 | 
				
			||||||
 | 
					            name=hnd_upgrade_name.text,
 | 
				
			||||||
 | 
					            desc=hnd_desc.text,
 | 
				
			||||||
 | 
					            requires=require_list
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        tree_upgrades.append(upgrade)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    tiers = {}
 | 
				
			||||||
 | 
					    dependency_lines = []
 | 
				
			||||||
 | 
					    for upgrade in tree_upgrades:
 | 
				
			||||||
 | 
					        deptier = 0
 | 
				
			||||||
 | 
					        buf_mmd.write(f"    {upgrade.id}@{{label: \"{upgrade.name}\"}}\n")
 | 
				
			||||||
 | 
					        collected_tiers = []
 | 
				
			||||||
 | 
					        for require in upgrade.requires:
 | 
				
			||||||
 | 
					            if require in tiers:
 | 
				
			||||||
 | 
					                collected_tiers.append(tiers[require])
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            dependency_lines.append(f"    {require} --> {upgrade.id}\n")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if len(collected_tiers) > 0:
 | 
				
			||||||
 | 
					            deptier = max(collected_tiers)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        tiers[upgrade.id] = deptier + 1
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    for line in dependency_lines:
 | 
				
			||||||
 | 
					        buf_mmd.write(line)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    buf_mmd.seek(0)
 | 
				
			||||||
 | 
					    print(buf_mmd.read())
 | 
				
			||||||
 | 
					    return get_upgrade_tree_mmd(tree) # TEMP
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_upgrade_tree_mmd(tree):
 | 
				
			||||||
 | 
					    if not gamedata.vfs.exists(f"upgrades/{tree}.mmd"):
 | 
				
			||||||
 | 
					        return flask.make_response("No Upgrade Tree", 404)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    with gamedata.vfs.open(f"upgrades/{tree}.mmd") as fd_upgradetree:
 | 
				
			||||||
 | 
					        mmd_upgradetree = mermaid.Mermaid(fd_upgradetree.read(), height=400)
 | 
				
			||||||
 | 
					        return mmd_upgradetree.svg_response.content
 | 
				
			||||||
@@ -1,3 +1,3 @@
 | 
				
			|||||||
pyqt6>=6.9.1
 | 
					pyqt6>=6.9.1
 | 
				
			||||||
pyqtwebengine>=5.15.7
 | 
					pyqt6-webengine>=6.9.0
 | 
				
			||||||
pywebview[qt]>=5.4
 | 
					pywebview[qt6]>=5.4
 | 
				
			||||||
@@ -1,4 +1,6 @@
 | 
				
			|||||||
Flask>=3.1.1
 | 
					Flask>=3.1.1
 | 
				
			||||||
 | 
					flask-cors>=6.0.1
 | 
				
			||||||
gevent>=25.5.1
 | 
					gevent>=25.5.1
 | 
				
			||||||
lxml>=6.0.0
 | 
					lxml>=6.0.0
 | 
				
			||||||
fs>=2.4.16
 | 
					fs>=2.4.16
 | 
				
			||||||
 | 
					mermaid-py==0.8.0
 | 
				
			||||||
@@ -1,19 +0,0 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
					 | 
				
			||||||
<UpgradeRules xmlns="seagull:rules/upgrades">
 | 
					 | 
				
			||||||
    <Upgrade>
 | 
					 | 
				
			||||||
        <Id>speed1</Id>
 | 
					 | 
				
			||||||
        <Name>Speedier Seagull</Name>
 | 
					 | 
				
			||||||
        <Modifiers>
 | 
					 | 
				
			||||||
            <Mod Id="up_speed1" Name="Upgrade: Speedier Seagull" Speed="5" />
 | 
					 | 
				
			||||||
        </Modifiers>
 | 
					 | 
				
			||||||
        <Desc>You become just a little bit faster, which makes it easier to steal things before your prey's previous owners notice you're coming.</Desc>
 | 
					 | 
				
			||||||
    </Upgrade>
 | 
					 | 
				
			||||||
    <Upgrade>
 | 
					 | 
				
			||||||
        <Id>theft_chance1</Id>
 | 
					 | 
				
			||||||
        <Name>Swooping Techniques</Name>
 | 
					 | 
				
			||||||
        <Modifiers>
 | 
					 | 
				
			||||||
            <Mod Id="up_theft_chance1" Name="Upgrade: Swooping Techniques" ChanceSteal="10" />
 | 
					 | 
				
			||||||
        </Modifiers>
 | 
					 | 
				
			||||||
        <Desc>It's all in the neck. The wings are just the steering wheel. You gain a bonus on all dice rolls for stealing.</Desc>
 | 
					 | 
				
			||||||
    </Upgrade>
 | 
					 | 
				
			||||||
</UpgradeRules>
 | 
					 | 
				
			||||||
@@ -1,20 +0,0 @@
 | 
				
			|||||||
<div id="charsheet-leftside">
 | 
					 | 
				
			||||||
    <div class="attr" id="attr-agility">
 | 
					 | 
				
			||||||
        Agility: <span id="lbl-attr-agility">0</span>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
    <div class="attr" id="attr-instinct">
 | 
					 | 
				
			||||||
        <span id="lbl-attr-instinct-txt">Instinct</span>: <span id="lbl-attr-instinct">0</span>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
<div id="charsheet-rightside">
 | 
					 | 
				
			||||||
    <div id="charsheet-upgrade-tabbar">
 | 
					 | 
				
			||||||
        <nav id="nav-upgrades"><ul>
 | 
					 | 
				
			||||||
            <li><button id="btn-upgrade-agility">Agility Upgrades</button></li>
 | 
					 | 
				
			||||||
            <li><button id="btn-upgrade-instinct">Instinct Upgrades</button></li>
 | 
					 | 
				
			||||||
        </ul></nav>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
    <div id="charsheet-upgrade-tree">
 | 
					 | 
				
			||||||
        <pre class="mermaid" id="upgrade-tree">
 | 
					 | 
				
			||||||
        </pre>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
@@ -1,12 +0,0 @@
 | 
				
			|||||||
---
 | 
					 | 
				
			||||||
title: Agility Upgrades
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
flowchart LR
 | 
					 | 
				
			||||||
    speed1["Speedier Seagull"]
 | 
					 | 
				
			||||||
    speed2["Greased Wings"]
 | 
					 | 
				
			||||||
    theft_chance1["Swooping Techniques"]
 | 
					 | 
				
			||||||
    theft_chance2["The Element of Surprise"]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    speed1-->speed2
 | 
					 | 
				
			||||||
    theft_chance1-->speed1
 | 
					 | 
				
			||||||
    theft_chance1-->theft_chance2
 | 
					 | 
				
			||||||
							
								
								
									
										39
									
								
								cmake/FindCargo.cmake
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								cmake/FindCargo.cmake
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					# Find Cargo, possibly in ~/.cargo. Make sure to check in any `bin` subdirectories
 | 
				
			||||||
 | 
					# on the program search path
 | 
				
			||||||
 | 
					# TODO: Remove the Unix-ism ($ENV{HOME}) and replace it with something platform-agnostic.
 | 
				
			||||||
 | 
					find_program(CARGO_EXECUTABLE cargo PATHS "$ENV{HOME}/.cargo" PATH_SUFFIXES bin)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					set(CARGO_VERSION "")
 | 
				
			||||||
 | 
					set(CARGO_CHANNEL "stable")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# If we found it, see if we can get its version.
 | 
				
			||||||
 | 
					if(CARGO_EXECUTABLE)
 | 
				
			||||||
 | 
					    execute_process(COMMAND ${CARGO_EXECUTABLE} -V OUTPUT_VARIABLE CARGO_VERSION_OUTPUT OUTPUT_STRIP_TRAILING_WHITESPACE)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if(CARGO_VERSION_OUTPUT MATCHES "cargo ([0-9]+\\.[0-9]+\\.[0-9]+).*")
 | 
				
			||||||
 | 
					        set(CARGO_VERSION ${CMAKE_MATCH_1})
 | 
				
			||||||
 | 
					    endif()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    execute_process(COMMAND ${CARGO_EXECUTABLE} -V OUTPUT_VARIABLE CARGO_CHANNEL_OUTPUT OUTPUT_STRIP_TRAILING_WHITESPACE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if(CARGO_CHANNEL_OUTPUT MATCHES "cargo [0-9]+\\.[0-9]+\\.[0-9]+-([a-zA-Z]*).*")
 | 
				
			||||||
 | 
					        set(CARGO_CHANNEL ${CMAKE_MATCH_1})
 | 
				
			||||||
 | 
					    endif()
 | 
				
			||||||
 | 
					endif()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Hides the CARGO_EXECUTABLE variable unless advanced variables are requested
 | 
				
			||||||
 | 
					mark_as_advanced(CARGO_EXECUTABLE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Require that we find both the executable and the version. Otherwise error out.
 | 
				
			||||||
 | 
					include(FindPackageHandleStandardArgs)
 | 
				
			||||||
 | 
					find_package_handle_standard_args(
 | 
				
			||||||
 | 
					    Cargo
 | 
				
			||||||
 | 
					    REQUIRED_VARS
 | 
				
			||||||
 | 
					        CARGO_VERSION
 | 
				
			||||||
 | 
					        CARGO_CHANNEL
 | 
				
			||||||
 | 
					        CARGO_EXECUTABLE
 | 
				
			||||||
 | 
					    VERSION_VAR
 | 
				
			||||||
 | 
					        CARGO_VERSION
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										1
									
								
								ext/jothepro/doxygen-awesome-css
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
							
						
						
									
										1
									
								
								ext/jothepro/doxygen-awesome-css
									
									
									
									
									
										Submodule
									
								
							 Submodule ext/jothepro/doxygen-awesome-css added at c085b886e3
									
								
							
							
								
								
									
										110
									
								
								libexec/doxygen.js
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										110
									
								
								libexec/doxygen.js
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,110 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env node
 | 
				
			||||||
 | 
					// https://gist.github.com/dimitarcl/3767879
 | 
				
			||||||
 | 
					var fs = require('fs')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var functionName = /^\s+\/\/\/\s+@function\s+(.*)$/;
 | 
				
			||||||
 | 
					var type = /^(\s+)\/\/\/\s+@param\s+{(\w*)}\s+(.+?)(\s+.*)$/;
 | 
				
			||||||
 | 
					var param = /^(\s+)\/\/\/\s+@param\s+(.+?)\s/;
 | 
				
			||||||
 | 
					var resultType = /^(\s+)\/\/\/\s+@return\s+{(\w+)}(\s+.*)$/;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Section()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						this.name = '';
 | 
				
			||||||
 | 
						this.result = 'undefined';
 | 
				
			||||||
 | 
						this.args = [];
 | 
				
			||||||
 | 
						this.comments = [];
 | 
				
			||||||
 | 
						this.namespaces = [];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Section.prototype.handle_function = function (line) {
 | 
				
			||||||
 | 
						this.namespaces = line.match(functionName)[1].split('.') || [];
 | 
				
			||||||
 | 
						this.name = this.namespaces.pop();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Section.prototype.handle_param = function (line) {
 | 
				
			||||||
 | 
						var paramType = 'Object';
 | 
				
			||||||
 | 
						var name = '';
 | 
				
			||||||
 | 
						var m = line.match(type);
 | 
				
			||||||
 | 
						var r = line;
 | 
				
			||||||
 | 
						if (m) {
 | 
				
			||||||
 | 
							paramType = m[2];
 | 
				
			||||||
 | 
							name = m[3];
 | 
				
			||||||
 | 
							r = m[1] + '/// @param ' + name + m[4];
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else {
 | 
				
			||||||
 | 
							m = line.match(param);
 | 
				
			||||||
 | 
							name = m[2];
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						this.args.push({name: name, type: paramType});
 | 
				
			||||||
 | 
						this.comments.push(r);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Section.prototype.handle_return = function (line) {
 | 
				
			||||||
 | 
						this.result = 'undefined';
 | 
				
			||||||
 | 
						var m = line.match(resultType);
 | 
				
			||||||
 | 
						var r = line;
 | 
				
			||||||
 | 
						if (m) {
 | 
				
			||||||
 | 
							this.result = m[2];
 | 
				
			||||||
 | 
							r = m[1] + '/// @return ' + m[3];
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						this.comments.push(r);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Section.prototype.Generate = function () {
 | 
				
			||||||
 | 
						var doc = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this.namespaces.forEach(function (namespace) {
 | 
				
			||||||
 | 
							doc.push('namespace ' + namespace + ' {\n');
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this.comments.forEach(function (c) {
 | 
				
			||||||
 | 
							doc.push(c);
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var args = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this.args.forEach(function (argument) {
 | 
				
			||||||
 | 
							args.push(argument.type + ' ' + argument.name);
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (this.name) {
 | 
				
			||||||
 | 
							doc.push(this.result + ' ' + this.name + '(' + args.join(', ') + ');');
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this.namespaces.forEach(function (namespace) {
 | 
				
			||||||
 | 
								doc.push('}\n');
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						return doc.join('\n');
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Section.prototype.handle_line = function (line) {
 | 
				
			||||||
 | 
						this.comments.push(line);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function writeLine(line) {
 | 
				
			||||||
 | 
						process.stdout.write(line + '\n');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fs.readFile(process.argv[2], 'utf8', function (err, data) {
 | 
				
			||||||
 | 
						var lines = data.split('\n');
 | 
				
			||||||
 | 
						var comment = /^\s*\/\/\//;
 | 
				
			||||||
 | 
						var directive = /@(\w+)\s+(.*)$/;
 | 
				
			||||||
 | 
						var inside = false;
 | 
				
			||||||
 | 
						var section = new Section();
 | 
				
			||||||
 | 
						lines.forEach(function(line) {
 | 
				
			||||||
 | 
							if (line.match(comment)) {
 | 
				
			||||||
 | 
								var d = line.match(directive);
 | 
				
			||||||
 | 
								if (d) {
 | 
				
			||||||
 | 
									var handle = Section.prototype['handle_' + d[1]] || Section.prototype.handle_line;
 | 
				
			||||||
 | 
									handle.call(section, line);
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									section.handle_line(line);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								inside = true;
 | 
				
			||||||
 | 
							} else if (inside) {
 | 
				
			||||||
 | 
								writeLine(section.Generate());
 | 
				
			||||||
 | 
								inside = false;
 | 
				
			||||||
 | 
								section = new Section();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										45
									
								
								pak/rules/upgrades/agility.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								pak/rules/upgrades/agility.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<UpgradeRules xmlns="seagull:rules/upgrades">
 | 
				
			||||||
 | 
					    <TreeData>
 | 
				
			||||||
 | 
					        <Name>Agility Upgrades</Name>
 | 
				
			||||||
 | 
					        <PrimaryColor>#00aa00</PrimaryColor>
 | 
				
			||||||
 | 
					    </TreeData>
 | 
				
			||||||
 | 
					    <Upgrade>
 | 
				
			||||||
 | 
					        <Id>speed1</Id>
 | 
				
			||||||
 | 
					        <Name>Speedier Seagull</Name>
 | 
				
			||||||
 | 
					        <Modifiers>
 | 
				
			||||||
 | 
					            <Mod Id="up_speed1" Name="Upgrade: Speedier Seagull" Speed="5" />
 | 
				
			||||||
 | 
					        </Modifiers>
 | 
				
			||||||
 | 
					        <Desc>You become just a little bit faster, which makes it easier to steal things before your prey's previous owners notice you're coming.</Desc>
 | 
				
			||||||
 | 
					    </Upgrade>
 | 
				
			||||||
 | 
					    <Upgrade>
 | 
				
			||||||
 | 
					        <Id>speed2</Id>
 | 
				
			||||||
 | 
					        <Name>Greased Wings</Name>
 | 
				
			||||||
 | 
					        <Desc>Applying a thin coat of old french fry oil makes you much faster. Why do humans throw this stuff out?</Desc>
 | 
				
			||||||
 | 
					        <Requirements>
 | 
				
			||||||
 | 
					            <Require>speed1</Require>
 | 
				
			||||||
 | 
					        </Requirements>
 | 
				
			||||||
 | 
					        <Modifiers>
 | 
				
			||||||
 | 
					            <Mod Id="up_speed2" Name="Upgrade: Greased Wings" Speed="10" />
 | 
				
			||||||
 | 
					        </Modifiers>
 | 
				
			||||||
 | 
					    </Upgrade>
 | 
				
			||||||
 | 
					    <Upgrade>
 | 
				
			||||||
 | 
					        <Id>theft_chance1</Id>
 | 
				
			||||||
 | 
					        <Name>Swooping Techniques</Name>
 | 
				
			||||||
 | 
					        <Modifiers>
 | 
				
			||||||
 | 
					            <Mod Id="up_theft_chance1" Name="Upgrade: Swooping Techniques" ChanceSteal="10" />
 | 
				
			||||||
 | 
					        </Modifiers>
 | 
				
			||||||
 | 
					        <Desc>It's all in the neck. The wings are just the steering wheel. You gain a bonus on all dice rolls for stealing.</Desc>
 | 
				
			||||||
 | 
					    </Upgrade>
 | 
				
			||||||
 | 
					    <Upgrade>
 | 
				
			||||||
 | 
					        <Id>theft_chance2</Id>
 | 
				
			||||||
 | 
					        <Name>The Element of Surprise</Name>
 | 
				
			||||||
 | 
					        <Desc>It's a lot easier to steal things if the previous owner doesn't see you coming. This technique gives you a bigger bonus on stealing rolls.</Desc>
 | 
				
			||||||
 | 
					        <Requirements>
 | 
				
			||||||
 | 
					            <Require>theft_chance1</Require>
 | 
				
			||||||
 | 
					        </Requirements>
 | 
				
			||||||
 | 
					        <Modifiers>
 | 
				
			||||||
 | 
					            <Mod Id="up_theft_chance2" Name="Upgrade: The Element of Surprise" ChanceSteal="15" />
 | 
				
			||||||
 | 
					        </Modifiers>
 | 
				
			||||||
 | 
					    </Upgrade>
 | 
				
			||||||
 | 
					</UpgradeRules>
 | 
				
			||||||
@@ -1,5 +1,7 @@
 | 
				
			|||||||
html, body { height: 100% }
 | 
					html, body { height: 100% }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** MAIN GAME **/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
div#root {
 | 
					div#root {
 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
    width: 100%;
 | 
					    width: 100%;
 | 
				
			||||||
@@ -50,6 +52,7 @@ div#main-day-stats {
 | 
				
			|||||||
    margin-top: auto;
 | 
					    margin-top: auto;
 | 
				
			||||||
    margin-bottom: auto;
 | 
					    margin-bottom: auto;
 | 
				
			||||||
    vertical-align: middle;
 | 
					    vertical-align: middle;
 | 
				
			||||||
 | 
					    font-size: large;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
div#main-button-bar {
 | 
					div#main-button-bar {
 | 
				
			||||||
@@ -101,37 +104,129 @@ div#charsheet {
 | 
				
			|||||||
    background-color: rgb(240, 240, 240);
 | 
					    background-color: rgb(240, 240, 240);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
div#charsheet-leftside {
 | 
					/** MODAL **/
 | 
				
			||||||
    display: flex;
 | 
					 | 
				
			||||||
    flex-direction: column;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
div#charsheet-rightside {
 | 
					 | 
				
			||||||
    display: flex;
 | 
					 | 
				
			||||||
    flex-direction: column;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
div.attr {
 | 
					 | 
				
			||||||
    font-size: larger;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
div#modal-background {
 | 
					div#modal-background {
 | 
				
			||||||
    z-index: -10;
 | 
					    font-family: sans-serif;
 | 
				
			||||||
    background-color: rgba(0, 0, 0, 168);
 | 
					    background-color: rgba(0, 0, 0, 0.6);
 | 
				
			||||||
    visibility: hidden;
 | 
					    
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    height: 100%;
 | 
				
			||||||
 | 
					    position: fixed;
 | 
				
			||||||
 | 
					    top: 0;
 | 
				
			||||||
 | 
					    left: 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
div#modal {
 | 
					div#modal {
 | 
				
			||||||
    width: 90%;
 | 
					    width: 75%;
 | 
				
			||||||
    height: 90%;
 | 
					    height: 75%;
 | 
				
			||||||
    border: 1.25em double rgba(192, 192, 192, 255);
 | 
					    margin: auto;
 | 
				
			||||||
 | 
					    margin-top: 50px;
 | 
				
			||||||
 | 
					    border: 0.25em dotted rgba(192, 192, 192, 255);
 | 
				
			||||||
    background-color: rgba(255, 255, 255, 255);
 | 
					    background-color: rgba(255, 255, 255, 255);
 | 
				
			||||||
 | 
					    padding: 0.3em
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
button.main-bar {
 | 
					button.main-bar {
 | 
				
			||||||
    width: 2.5em;
 | 
					    width: 2.5em;
 | 
				
			||||||
    height: 2.5em;
 | 
					    height: 2.5em;
 | 
				
			||||||
 | 
					    margin: 2.5px;
 | 
				
			||||||
    background-color: rgba(0,0,0,0);
 | 
					    background-color: rgba(0,0,0,0);
 | 
				
			||||||
    border: 1px solid black;
 | 
					    border: 1px solid black;
 | 
				
			||||||
    font-size: 2em;
 | 
					    font-size: 2em;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					button#button-modal-close {
 | 
				
			||||||
 | 
					    position: fixed;
 | 
				
			||||||
 | 
					    top: 0;
 | 
				
			||||||
 | 
					    right: 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    width: 2.5em;
 | 
				
			||||||
 | 
					    height: 2.5em;
 | 
				
			||||||
 | 
					    margin: 2.5px;
 | 
				
			||||||
 | 
					    background-color: rgba(0,0,0,0);
 | 
				
			||||||
 | 
					    border: 0px;
 | 
				
			||||||
 | 
					    font-size: 2em;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** CHARSHEET **/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					div#charsheet-root {
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					    flex-direction: row;
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    height: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					div#charsheet-leftside {
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					    flex-direction: column;
 | 
				
			||||||
 | 
					    width: 33%;
 | 
				
			||||||
 | 
					    height: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					div.attr {
 | 
				
			||||||
 | 
					    /* common to all attribute blocks */
 | 
				
			||||||
 | 
					    font-size: x-large;
 | 
				
			||||||
 | 
					    color: #ffffff;
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    height: 30%;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    vertical-align: middle;
 | 
				
			||||||
 | 
					    margin-top: auto;
 | 
				
			||||||
 | 
					    margin-bottom: auto;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    text-align: center;
 | 
				
			||||||
 | 
					    margin-left: auto;
 | 
				
			||||||
 | 
					    margin-right: auto;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					div#attr-points {
 | 
				
			||||||
 | 
					    font-size: x-large;
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    height: 10%;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    vertical-align: middle;
 | 
				
			||||||
 | 
					    margin-top: auto;
 | 
				
			||||||
 | 
					    margin-bottom: auto;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    text-align: center;
 | 
				
			||||||
 | 
					    margin-left: auto;
 | 
				
			||||||
 | 
					    margin-right: auto;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					div#attr-agility {
 | 
				
			||||||
 | 
					    background: linear-gradient(to right, rgb(0,170,0), rgb(0,99,0));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					div#attr-instinct {
 | 
				
			||||||
 | 
					    background: linear-gradient(to right, rgb(170,0,255), rgb(90,0,135));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					div#attr-leadership {
 | 
				
			||||||
 | 
					    background: linear-gradient(to right, rgb(255,170,0), rgb(139,93,0));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					div#charsheet-rightside {
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					    flex-direction: column;
 | 
				
			||||||
 | 
					    width: 67%;
 | 
				
			||||||
 | 
					    height: 100%;
 | 
				
			||||||
 | 
					    overflow-x: scroll;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					nav#nav-upgrades {
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					    flex-direction: row;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					nav#nav-upgrades li {
 | 
				
			||||||
 | 
					    display: inline;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					div#charsheet-upgrade-tree {
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					    flex-wrap: nowrap;
 | 
				
			||||||
 | 
					    flex-direction: row;
 | 
				
			||||||
 | 
					    width: max-content;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
		 Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								pak/static/image/splash.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pak/static/image/splash.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 187 KiB  | 
							
								
								
									
										32
									
								
								pak/static/js/dlg-charsheet.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								pak/static/js/dlg-charsheet.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					var charsheet_elements = {}
 | 
				
			||||||
 | 
					charsheet_elements["lbl_agility"] = document.getElementById("lbl-attr-agility");
 | 
				
			||||||
 | 
					charsheet_elements["lbl_instinct"] = document.getElementById("lbl-attr-instinct");
 | 
				
			||||||
 | 
					charsheet_elements["lbl_leadership"] = document.getElementById("lbl-attr-leadership");
 | 
				
			||||||
 | 
					charsheet_elements["lbl_instinct_txt"] = document.getElementById("lbl-attr-instinct-txt");
 | 
				
			||||||
 | 
					charsheet_elements["btn_upgrade_agility"] = document.getElementById("btn-upgrade-agility");
 | 
				
			||||||
 | 
					charsheet_elements["btn_upgrade_instinct"] = document.getElementById("btn-upgrade-instinct");
 | 
				
			||||||
 | 
					charsheet_elements["btn_upgrade_leadership"] = document.getElementById("btn-upgrade-leadership");
 | 
				
			||||||
 | 
					charsheet_elements["blk_tree"] = document.getElementById("charsheet-upgrade-tree");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function update_charsheet() {
 | 
				
			||||||
 | 
					    charsheet_elements["lbl_agility"].innerHTML = gamestate["agility"];
 | 
				
			||||||
 | 
					    charsheet_elements["lbl_instinct"].innerHTML = gamestate["instinct"];
 | 
				
			||||||
 | 
					    charsheet_elements["lbl_leadership"].innerHTML = gamestate["leadership"];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (gamestate["story_beat"] >= 3) {
 | 
				
			||||||
 | 
					        charsheet_elements["lbl_instinct_txt"].innerHTML = "Intelligence";
 | 
				
			||||||
 | 
					        charsheet_elements["btn_upgrade_instinct"].innerHTML = "Intelligence Upgrades";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function display_tree(tree) {
 | 
				
			||||||
 | 
					    var upgrade_tree = await fetch(`/upgrades/${tree}`)
 | 
				
			||||||
 | 
					    .then(res => res.text())
 | 
				
			||||||
 | 
					    console.log(upgrade_tree)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    charsheet_elements["blk_tree"].innerHTML = upgrade_tree
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					charsheet_elements["btn_upgrade_agility"].addEventListener("click", (ev) => {display_tree("agility")});
 | 
				
			||||||
 | 
					charsheet_elements["btn_upgrade_instinct"].addEventListener("click", (ev) => {display_tree("instinct")});
 | 
				
			||||||
 | 
					charsheet_elements["btn_upgrade_leadership"].addEventListener("click", (ev) => {display_tree("leadership")});
 | 
				
			||||||
@@ -1,5 +1,4 @@
 | 
				
			|||||||
var desktop_mode = true;
 | 
					globalThis.desktop_mode = true;
 | 
				
			||||||
var tick_meter_running = false;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function prepare_gamestate() {
 | 
					async function prepare_gamestate() {
 | 
				
			||||||
    var gamestate_loaded = null;
 | 
					    var gamestate_loaded = null;
 | 
				
			||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
var desktop_mode = false;
 | 
					globalThis.desktop_mode = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function prepare_gamestate() {
 | 
					function load_gamestate() {
 | 
				
			||||||
    var gamestate_loaded = window.localStorage.getItem("gamestate");
 | 
					    var gamestate_loaded = window.localStorage.getItem("gamestate");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (gamestate_loaded == null) {
 | 
					    if (gamestate_loaded == null) {
 | 
				
			||||||
@@ -2,13 +2,16 @@ const ver_numeric = 0;
 | 
				
			|||||||
const ver_string = "pre alpha";
 | 
					const ver_string = "pre alpha";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const sleep = ms => new Promise(r => setTimeout(r, ms)); // sleep(int ms)
 | 
					const sleep = ms => new Promise(r => setTimeout(r, ms)); // sleep(int ms)
 | 
				
			||||||
 | 
					const avg = input => input.reduce((a,b) => a+b) / input.length; // avg([1,2,3...])
 | 
				
			||||||
 | 
					const urlExists = async url => (await fetch(url)).ok
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var page_elements = {};
 | 
					var page_elements = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var gamestate = {};
 | 
					globalThis.gamestate = {};
 | 
				
			||||||
 | 
					globalThis.tick_meter_running = false;
 | 
				
			||||||
var ticks_since_last_save = 0;
 | 
					var ticks_since_last_save = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const gamestate_default = {
 | 
					globalThis.gamestate_default = {
 | 
				
			||||||
    "statever": "1",
 | 
					    "statever": "1",
 | 
				
			||||||
    "tick": 1,
 | 
					    "tick": 1,
 | 
				
			||||||
    "name": "Nameless",
 | 
					    "name": "Nameless",
 | 
				
			||||||
@@ -25,11 +28,28 @@ const gamestate_default = {
 | 
				
			|||||||
    "enc_seagull": "pause",
 | 
					    "enc_seagull": "pause",
 | 
				
			||||||
    "agility": 0,
 | 
					    "agility": 0,
 | 
				
			||||||
    "instinct": 0,
 | 
					    "instinct": 0,
 | 
				
			||||||
    "leadership": 0
 | 
					    "leadership": 0,
 | 
				
			||||||
 | 
					    "income": {
 | 
				
			||||||
 | 
					        "last_food": Array(10).fill(0),
 | 
				
			||||||
 | 
					        "last_shinies": Array(10).fill(0),
 | 
				
			||||||
 | 
					        "calc_food": 0,
 | 
				
			||||||
 | 
					        "calc_shinies": 0
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "modifiers": {
 | 
				
			||||||
 | 
					        "speed": [],
 | 
				
			||||||
 | 
					        "chancesteal": []
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "upgrades": []
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const tickdiffs_reset = {
 | 
				
			||||||
 | 
					    "food": 0,
 | 
				
			||||||
 | 
					    "shinies": 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					var tickdiffs = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var bool_log_alt = false
 | 
					var bool_log_alt = false
 | 
				
			||||||
function record_log(text) {
 | 
					globalThis.record_log = function (text) {
 | 
				
			||||||
    const div_logrow = document.createElement("div");
 | 
					    const div_logrow = document.createElement("div");
 | 
				
			||||||
    if (bool_log_alt) { div_logrow.className = "log-line"; }
 | 
					    if (bool_log_alt) { div_logrow.className = "log-line"; }
 | 
				
			||||||
    else { div_logrow.className = "log-line-alt"; }
 | 
					    else { div_logrow.className = "log-line-alt"; }
 | 
				
			||||||
@@ -87,37 +107,91 @@ function record_log_with_choices() {
 | 
				
			|||||||
    tick_meter_running = false;
 | 
					    tick_meter_running = false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var modal_dialog_open = false;
 | 
					globalThis.modal_dialog_open = false;
 | 
				
			||||||
 | 
					globalThis.modal_dialog_scripted = false;
 | 
				
			||||||
 | 
					globalThis.modal_dialog_name = "";
 | 
				
			||||||
var dialog_queue = [];
 | 
					var dialog_queue = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function modal_no_prop(event) { event.stopPropagation(); }
 | 
					function modal_no_prop(event) { event.stopPropagation(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function open_modal_dialog(dialog) {
 | 
					async function open_modal_dialog(dialog) {
 | 
				
			||||||
    var modal_background = document.getElementById("modal-background");
 | 
					 | 
				
			||||||
    if (!modal_background) {
 | 
					 | 
				
			||||||
        modal_background = document.createElement("div");
 | 
					 | 
				
			||||||
        modal_background.setAttribute("id", "modal-background");
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    var modal_root = document.getElementById("modal");
 | 
					 | 
				
			||||||
    if (!modal_root) {
 | 
					 | 
				
			||||||
        modal_root = document.createElement("div");
 | 
					 | 
				
			||||||
        modal_root.setAttribute("id", "modal");
 | 
					 | 
				
			||||||
        modal_root.onclick = modal_no_prop;
 | 
					 | 
				
			||||||
        modal_background.appendChild(modal_root);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!modal_dialog_open) {
 | 
					    if (!modal_dialog_open) {
 | 
				
			||||||
        tick_meter_running = false;
 | 
					        tick_meter_running = false;
 | 
				
			||||||
        modal_dialog_open = true;
 | 
					        modal_dialog_open = true;
 | 
				
			||||||
        modal_background.style.zIndex = "10 !important";
 | 
					        modal_dialog_name = dialog;
 | 
				
			||||||
        modal_background.style.visibility = "visible !important";
 | 
					
 | 
				
			||||||
 | 
					        var modal_background = document.createElement("div");
 | 
				
			||||||
 | 
					        modal_background.setAttribute("id", "modal-background");
 | 
				
			||||||
 | 
					        modal_background.style.zIndex = "10";
 | 
				
			||||||
 | 
					        modal_background.style.visibility = "visible";
 | 
				
			||||||
 | 
					        modal_background = document.body.appendChild(modal_background);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var modal_close = document.createElement("button");
 | 
				
			||||||
 | 
					        modal_close.setAttribute("id", "button-modal-close");
 | 
				
			||||||
 | 
					        modal_close.innerHTML = "❌";
 | 
				
			||||||
 | 
					        modal_close.addEventListener("click", (ev) => {close_modal_dialog()});
 | 
				
			||||||
 | 
					        modal_close = modal_background.appendChild(modal_close);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var modal_root = document.createElement("div");
 | 
				
			||||||
 | 
					        modal_root.setAttribute("id", "modal");
 | 
				
			||||||
 | 
					        modal_root.onclick = modal_no_prop;
 | 
				
			||||||
 | 
					        modal_root = modal_background.appendChild(modal_root);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var dialog_data = await fetch(`/dialog/${dialog}`)
 | 
				
			||||||
 | 
					        .then(res => { return res.text(); });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        modal_root.innerHTML = dialog_data;
 | 
				
			||||||
 | 
					        if (urlExists(`/static/js/dlg-${dialog}.js`)) {
 | 
				
			||||||
 | 
					            //*
 | 
				
			||||||
 | 
					            var script = document.createElement("script");
 | 
				
			||||||
 | 
					            script.setAttribute("id", "dialog-script");
 | 
				
			||||||
 | 
					            script.src = `/static/js/dlg-${dialog}.js`;
 | 
				
			||||||
 | 
					            document.head.appendChild(script);
 | 
				
			||||||
 | 
					            modal_dialog_scripted = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        var dialog_data = await fetch(`/dialog/${dialog}`)
 | 
				
			||||||
 | 
					        .then(res => { return res.text(); });
 | 
				
			||||||
 | 
					        var dialog_script = null;
 | 
				
			||||||
 | 
					        if (urlExists(`/static/js/dlg-${dialog}.js`)) {
 | 
				
			||||||
 | 
					            dialog_script = `/static/js/dlg-${dialog}.js`;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        dialog_queue.push([dialog_data, dialog_script, dialog]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function close_modal_dialog() {
 | 
				
			||||||
 | 
					    if (!modal_dialog_open) { return; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var modal_background = document.getElementById("modal-background");
 | 
				
			||||||
 | 
					    var modal_root = document.getElementById("modal-root");
 | 
				
			||||||
 | 
					    var dialog_script = document.getElementById("dialog-script");
 | 
				
			||||||
 | 
					    if (dialog_script) {
 | 
				
			||||||
 | 
					        document.head.removeChild(dialog_script);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    dialog_data = await fetch(`/dialog/${dialog}`)
 | 
					    if (dialog_queue.length == 0) {
 | 
				
			||||||
    .then(res => { return res.text(); });
 | 
					        modal_background.style.zIndex = "-10";
 | 
				
			||||||
 | 
					        modal_background.style.visibility = "hidden";
 | 
				
			||||||
 | 
					        document.body.removeChild(modal_background);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    modal_root.innerHTML = dialog_data;
 | 
					        tick_meter_running = true;
 | 
				
			||||||
 | 
					        modal_dialog_open = false;
 | 
				
			||||||
 | 
					        modal_dialog_name = "";
 | 
				
			||||||
 | 
					        modal_dialog_scripted = false;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        next_dialog = dialog_queue.pop();
 | 
				
			||||||
 | 
					        modal_root.innerHTML = next_dialog[0];
 | 
				
			||||||
 | 
					        modal_dialog_name = next_dialog[2];
 | 
				
			||||||
 | 
					        if (next_dialog[1]) {
 | 
				
			||||||
 | 
					            script = document.createElement("script");
 | 
				
			||||||
 | 
					            script.setAttribute("id", "dialog-script");
 | 
				
			||||||
 | 
					            script.src = next_dialog[1];
 | 
				
			||||||
 | 
					            document.head.appendChild(script);
 | 
				
			||||||
 | 
					            modal_dialog_scripted = true;
 | 
				
			||||||
 | 
					        } else { modal_dialog_scripted = false; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function update_ui() {
 | 
					function update_ui() {
 | 
				
			||||||
@@ -126,6 +200,8 @@ function update_ui() {
 | 
				
			|||||||
    page_elements["lbl_colony"].innerHTML = gamestate["colony"];
 | 
					    page_elements["lbl_colony"].innerHTML = gamestate["colony"];
 | 
				
			||||||
    page_elements["lbl_shinies"].innerHTML = gamestate["shinies"].toFixed(2);
 | 
					    page_elements["lbl_shinies"].innerHTML = gamestate["shinies"].toFixed(2);
 | 
				
			||||||
    page_elements["lbl_food"].innerHTML = gamestate["food"].toFixed(2);
 | 
					    page_elements["lbl_food"].innerHTML = gamestate["food"].toFixed(2);
 | 
				
			||||||
 | 
					    page_elements["lbl_inc_food"].innerHTML = gamestate["income"]["calc_food"].toFixed(2);
 | 
				
			||||||
 | 
					    page_elements["lbl_inc_shinies"].innerHTML = gamestate["income"]["calc_shinies"].toFixed(2);
 | 
				
			||||||
    page_elements["lbl_class"].innerHTML = gamestate["class"];
 | 
					    page_elements["lbl_class"].innerHTML = gamestate["class"];
 | 
				
			||||||
    page_elements["lbl_xp"].innerHTML = gamestate["xp"];
 | 
					    page_elements["lbl_xp"].innerHTML = gamestate["xp"];
 | 
				
			||||||
    page_elements["lbl_xp_next"].innerHTML = gamestate["xp_next"];
 | 
					    page_elements["lbl_xp_next"].innerHTML = gamestate["xp_next"];
 | 
				
			||||||
@@ -160,10 +236,11 @@ function dev_toolbox(open) {
 | 
				
			|||||||
function reward_xp(amount) {
 | 
					function reward_xp(amount) {
 | 
				
			||||||
    gamestate["xp"] += amount;
 | 
					    gamestate["xp"] += amount;
 | 
				
			||||||
    if (gamestate["xp"] >= gamestate["xp_next"]) {
 | 
					    if (gamestate["xp"] >= gamestate["xp_next"]) {
 | 
				
			||||||
        old_xp_next = gamestate["xp_next"];
 | 
					        var old_xp_next = gamestate["xp_next"];
 | 
				
			||||||
        gamestate["xp"] -= old_xp_next;
 | 
					        gamestate["xp"] -= old_xp_next;
 | 
				
			||||||
        gamestate["level"] += 1;
 | 
					        gamestate["level"] += 1;
 | 
				
			||||||
        gamestate["xp_next"] = (old_xp_next * 1.5) + (gamestate["level"] * 5);
 | 
					        gamestate["xp_next"] = (old_xp_next * 1.5) + (gamestate["level"] * 5);
 | 
				
			||||||
 | 
					        record_log(`You have advanced to level ${gamestate["level"]}.`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (gamestate["level"] == 2) {
 | 
					        if (gamestate["level"] == 2) {
 | 
				
			||||||
            gamestate["story_beat"] = 1;
 | 
					            gamestate["story_beat"] = 1;
 | 
				
			||||||
@@ -176,21 +253,22 @@ function reward_xp(amount) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function steal_resource(resource, target, amount, itemstr) {
 | 
					globalThis.steal_resource = async function (resource, target, amount, itemstr) {
 | 
				
			||||||
    var items = itemstr.split(",")
 | 
					    var items = itemstr.split(",")
 | 
				
			||||||
    var stealdata = await fetch(`/act/steal/${resource}/${target}`, {method: "POST", body: JSON.stringify({gamestate: gamestate})})
 | 
					    var stealdata = await fetch(`/act/steal/${resource}/${target}`, {method: "POST", headers: {"Content-Type": "application/json"},body: JSON.stringify({gamestate: gamestate})})
 | 
				
			||||||
    .then(res => { return res.json(); })
 | 
					    .then(res => { return res.json(); })
 | 
				
			||||||
    .catch(e => { throw e; });
 | 
					    .catch(e => { throw e; });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (stealdata["success"] && amount > 0) {
 | 
					    if (stealdata["success"] && amount > 0) {
 | 
				
			||||||
        gamestate[resource] += amount;
 | 
					        gamestate[resource] += amount;
 | 
				
			||||||
 | 
					        tickdiffs[resource] += amount;
 | 
				
			||||||
        reward_xp(2);
 | 
					        reward_xp(2);
 | 
				
			||||||
        record_log(`Stole ${resource} from a ${target}: ${items.join(", ")}`);
 | 
					        record_log(`Stole ${resource} from a ${target}: ${items.join(", ")}`);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    else { record_log(`Didn't steal ${resource} from a ${target}`); }
 | 
					    else { record_log(`Didn't steal ${resource} from a ${target}`); }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function recruit(amount) {
 | 
					globalThis.recruit = async function (amount) {
 | 
				
			||||||
    if (gamestate["shinies"] < amount) {
 | 
					    if (gamestate["shinies"] < amount) {
 | 
				
			||||||
        record_log("You do not have enough shinies to recruit this seagull.");
 | 
					        record_log("You do not have enough shinies to recruit this seagull.");
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
@@ -208,13 +286,53 @@ async function recruit(amount) {
 | 
				
			|||||||
    else { record_log("The other gull wasn't impressed. Recruiting failed."); }
 | 
					    else { record_log("The other gull wasn't impressed. Recruiting failed."); }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const hnd_devtoolkit = new Konami(() => {
 | 
				
			||||||
 | 
					    if (modal_dialog_name == "about") {
 | 
				
			||||||
 | 
					        close_modal_dialog();
 | 
				
			||||||
 | 
					        dev_toolbox(true);
 | 
				
			||||||
 | 
					        var snd = new Audio("/static/sound/open_dev_toolkit.wav");
 | 
				
			||||||
 | 
					        snd.play();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function game_tick() {
 | 
					async function game_tick() {
 | 
				
			||||||
    gamestate["tick"] += 1;
 | 
					    gamestate["tick"] += 1;
 | 
				
			||||||
    ticks_since_last_save += 1;
 | 
					    ticks_since_last_save += 1;
 | 
				
			||||||
    page_elements["lbl_tick"].innerHTML = gamestate["tick"];
 | 
					    page_elements["lbl_tick"].innerHTML = gamestate["tick"];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (gamestate["tick"] % 5 == 0) {
 | 
				
			||||||
 | 
					        var colony_tickdata = await fetch("/tick/colony", {
 | 
				
			||||||
 | 
					            method: "POST",
 | 
				
			||||||
 | 
					            body: JSON.stringify({
 | 
				
			||||||
 | 
					                colony: gamestate["colony"] - 1,
 | 
				
			||||||
 | 
					                modifiers: [],
 | 
				
			||||||
 | 
					                avg_food: gamestate["income"]["calc_food"],
 | 
				
			||||||
 | 
					                avg_shinies: gamestate["income"]["calc_shinies"]
 | 
				
			||||||
 | 
					            }),
 | 
				
			||||||
 | 
					            headers: {
 | 
				
			||||||
 | 
					                "Content-Type": "application/json"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .then(res => {
 | 
				
			||||||
 | 
					            var json = res.json()
 | 
				
			||||||
 | 
					            console.log(json)
 | 
				
			||||||
 | 
					            return json
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .catch(e => {throw e;});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (colony_tickdata["success"]) {
 | 
				
			||||||
 | 
					            gamestate["food"] += colony_tickdata["food"];
 | 
				
			||||||
 | 
					            tickdiffs["food"] += colony_tickdata["food"];
 | 
				
			||||||
 | 
					            gamestate["shinies"] += colony_tickdata["shinies"];
 | 
				
			||||||
 | 
					            tickdiffs["shinies"] += colony_tickdata["shinies"];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            record_log(`Your colony provides you with ${colony_tickdata["food"].toFixed(2)} food and ${colony_tickdata["shinies"].toFixed(2)} shinies.`);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var tickdata = await fetch("/tick")
 | 
					    var tickdata = await fetch("/tick")
 | 
				
			||||||
    .then(res => {
 | 
					    .then(res => {
 | 
				
			||||||
        json = res.json()
 | 
					        var json = res.json()
 | 
				
			||||||
        console.log(json)
 | 
					        console.log(json)
 | 
				
			||||||
        return json
 | 
					        return json
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
@@ -352,6 +470,15 @@ async function game_tick() {
 | 
				
			|||||||
        ticks_since_last_save = 0;
 | 
					        ticks_since_last_save = 0;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    gamestate["income"]["last_food"].shift()
 | 
				
			||||||
 | 
					    gamestate["income"]["last_food"].push(tickdiffs["food"])
 | 
				
			||||||
 | 
					    gamestate["income"]["last_shinies"].shift()
 | 
				
			||||||
 | 
					    gamestate["income"]["last_shinies"].push(tickdiffs["shinies"])
 | 
				
			||||||
 | 
					    tickdiffs = structuredClone(tickdiffs_reset);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    gamestate["income"]["calc_food"] = avg(gamestate["income"]["last_food"])
 | 
				
			||||||
 | 
					    gamestate["income"]["calc_shinies"] = avg(gamestate["income"]["last_shinies"])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    update_ui();
 | 
					    update_ui();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -374,6 +501,8 @@ function update_action(enc, value) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
target.addEventListener(start_event, function (ev) {
 | 
					target.addEventListener(start_event, function (ev) {
 | 
				
			||||||
 | 
					    tickdiffs = structuredClone(tickdiffs_reset);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    page_elements["div_log"] = document.querySelector("#main-log");
 | 
					    page_elements["div_log"] = document.querySelector("#main-log");
 | 
				
			||||||
    page_elements["div_sidebar"] = document.querySelector("#main-sidebar");
 | 
					    page_elements["div_sidebar"] = document.querySelector("#main-sidebar");
 | 
				
			||||||
    page_elements["div_name"] = document.querySelector("#side-seagull-name");
 | 
					    page_elements["div_name"] = document.querySelector("#side-seagull-name");
 | 
				
			||||||
@@ -383,6 +512,8 @@ target.addEventListener(start_event, function (ev) {
 | 
				
			|||||||
    page_elements["lbl_colony"] = document.querySelector("#lbl-seagull-colony");
 | 
					    page_elements["lbl_colony"] = document.querySelector("#lbl-seagull-colony");
 | 
				
			||||||
    page_elements["lbl_shinies"] = document.querySelector("#lbl-seagull-shinies");
 | 
					    page_elements["lbl_shinies"] = document.querySelector("#lbl-seagull-shinies");
 | 
				
			||||||
    page_elements["lbl_food"] = document.querySelector("#lbl-seagull-food");
 | 
					    page_elements["lbl_food"] = document.querySelector("#lbl-seagull-food");
 | 
				
			||||||
 | 
					    page_elements["lbl_inc_food"] = document.querySelector("#lbl-seagull-food-income");
 | 
				
			||||||
 | 
					    page_elements["lbl_inc_shinies"] = document.querySelector("#lbl-seagull-shinies-income");
 | 
				
			||||||
    page_elements["edt_name"] = document.querySelector("#edt-seagull-name");
 | 
					    page_elements["edt_name"] = document.querySelector("#edt-seagull-name");
 | 
				
			||||||
    page_elements["lbl_tick"] = document.querySelector("#main-day-counter");
 | 
					    page_elements["lbl_tick"] = document.querySelector("#main-day-counter");
 | 
				
			||||||
    page_elements["lbl_xp"] = document.querySelector("#lbl-seagull-xp-current");
 | 
					    page_elements["lbl_xp"] = document.querySelector("#lbl-seagull-xp-current");
 | 
				
			||||||
@@ -397,6 +528,7 @@ target.addEventListener(start_event, function (ev) {
 | 
				
			|||||||
    page_elements["menu_enc_human"].addEventListener("change", (ev) => {update_action("human", ev.target.value)});
 | 
					    page_elements["menu_enc_human"].addEventListener("change", (ev) => {update_action("human", ev.target.value)});
 | 
				
			||||||
    page_elements["menu_enc_seagull"].addEventListener("change", (ev) => {update_action("seagull", ev.target.value)});
 | 
					    page_elements["menu_enc_seagull"].addEventListener("change", (ev) => {update_action("seagull", ev.target.value)});
 | 
				
			||||||
    page_elements["btn_charsheet"].addEventListener("click", (ev) => {open_modal_dialog("charsheet")});
 | 
					    page_elements["btn_charsheet"].addEventListener("click", (ev) => {open_modal_dialog("charsheet")});
 | 
				
			||||||
 | 
					    page_elements["btn_about"].addEventListener("click", (ev) => {open_modal_dialog("about")});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    prepare_gamestate();
 | 
					    prepare_gamestate();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								pak/static/sound/open_dev_toolkit.wav
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pak/static/sound/open_dev_toolkit.wav
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										16
									
								
								pak/templates/about.j2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								pak/templates/about.j2
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					<div id="about-root">
 | 
				
			||||||
 | 
					    <h1>Seagull Game</h1>
 | 
				
			||||||
 | 
					    <p>© 2025 Nicole O'Connor.</p>
 | 
				
			||||||
 | 
					    <p>
 | 
				
			||||||
 | 
					        Licensed under the Apache License, Version 2.0 (the "License"); 
 | 
				
			||||||
 | 
					        you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					        You may obtain a copy of the License at <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache.org</a>.
 | 
				
			||||||
 | 
					    </p>
 | 
				
			||||||
 | 
					    <p>
 | 
				
			||||||
 | 
					        Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					        distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					        See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					        limitations under the License. 
 | 
				
			||||||
 | 
					    </p>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
							
								
								
									
										27
									
								
								pak/templates/charsheet.j2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								pak/templates/charsheet.j2
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					<div id="charsheet-root">
 | 
				
			||||||
 | 
					    <div id="charsheet-leftside">
 | 
				
			||||||
 | 
					        <div id "attr-points">
 | 
				
			||||||
 | 
					            Available Points: <span id="lbl-attr-points">0</span>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="attr" id="attr-agility">
 | 
				
			||||||
 | 
					            Agility: <span id="lbl-attr-agility">0</span>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="attr" id="attr-instinct">
 | 
				
			||||||
 | 
					            <span id="lbl-attr-instinct-txt">Instinct</span>: <span id="lbl-attr-instinct">0</span>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="attr" id="attr-leadership">
 | 
				
			||||||
 | 
					            Leadership: <span id="lbl-attr-leadership">0</span>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div id="charsheet-rightside">
 | 
				
			||||||
 | 
					        <div id="charsheet-upgrade-tabbar">
 | 
				
			||||||
 | 
					            <nav id="nav-upgrades"><ul>
 | 
				
			||||||
 | 
					                <li><button id="btn-upgrade-agility">Agility Upgrades</button></li>
 | 
				
			||||||
 | 
					                <li><button id="btn-upgrade-instinct">Instinct Upgrades</button></li>
 | 
				
			||||||
 | 
					                <li><button id="btn-upgrade-leadership">Leadership Upgrades</button></li>
 | 
				
			||||||
 | 
					            </ul></nav>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div id="charsheet-upgrade-tree" class="mermaid">
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
@@ -7,7 +7,11 @@
 | 
				
			|||||||
<link rel="stylesheet" href="{{ style }}">
 | 
					<link rel="stylesheet" href="{{ style }}">
 | 
				
			||||||
{%- endfor -%}
 | 
					{%- endfor -%}
 | 
				
			||||||
{%- for script in scripts -%}
 | 
					{%- for script in scripts -%}
 | 
				
			||||||
<script src="{{ script }}"></script>
 | 
					{%- if script[1] -%}
 | 
				
			||||||
 | 
					<script type="module" src="{{ script[0] }}"></script>
 | 
				
			||||||
 | 
					{%- else -%}
 | 
				
			||||||
 | 
					<script src="{{ script[0] }}"></script>
 | 
				
			||||||
 | 
					{%- endif -%}
 | 
				
			||||||
{%- endfor -%}
 | 
					{%- endfor -%}
 | 
				
			||||||
</head>
 | 
					</head>
 | 
				
			||||||
<body>
 | 
					<body>
 | 
				
			||||||
@@ -28,8 +32,8 @@
 | 
				
			|||||||
        <p id="side-seagull-xp">XP: <span id="lbl-seagull-xp-current">0</span>/<span id="lbl-seagull-xp-next">0</span></p>
 | 
					        <p id="side-seagull-xp">XP: <span id="lbl-seagull-xp-current">0</span>/<span id="lbl-seagull-xp-next">0</span></p>
 | 
				
			||||||
        <p id="side-seagull-misc">
 | 
					        <p id="side-seagull-misc">
 | 
				
			||||||
            Colony: <span id="lbl-seagull-colony">1337</span><br />
 | 
					            Colony: <span id="lbl-seagull-colony">1337</span><br />
 | 
				
			||||||
            Shinies: <span id="lbl-seagull-shinies">420</span><br />
 | 
					            Shinies: <span id="lbl-seagull-shinies">420</span> <i>(+<span id="lbl-seagull-shinies-income">0</span>/day)</i><br />
 | 
				
			||||||
            Food: <span id="lbl-seagull-food">69</span>
 | 
					            Food: <span id="lbl-seagull-food">69</span> <i>(+<span id="lbl-seagull-food-income">0</span>/day)</i>
 | 
				
			||||||
        </p>
 | 
					        </p>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <hr />
 | 
					    <hr />
 | 
				
			||||||
							
								
								
									
										24
									
								
								pak/upgrades/agility.mmd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								pak/upgrades/agility.mmd
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					---
 | 
				
			||||||
 | 
					title: Agility Upgrades
 | 
				
			||||||
 | 
					theme: base
 | 
				
			||||||
 | 
					themeVariables:
 | 
				
			||||||
 | 
					    primaryColor: "#00aa00"
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					flowchart LR
 | 
				
			||||||
 | 
					    speed1@{label: "Speedier Seagull"}
 | 
				
			||||||
 | 
					    speed2@{label: "Greased Wings"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    theft_chance1@{label: "Swooping Techniques"}
 | 
				
			||||||
 | 
					    theft_chance2@{label: "The Element of Surprise"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    passive_shinies_income1@{label: "Get On The Floor"}
 | 
				
			||||||
 | 
					    passive_shinies_income2@{label: "Open The Door"}
 | 
				
			||||||
 | 
					    passive_shinies_income3@{label: "Walk Like A Dinosaur"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    speed1-->speed2
 | 
				
			||||||
 | 
					    theft_chance1-->speed1
 | 
				
			||||||
 | 
					    theft_chance1-->theft_chance2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    theft_chance1-->passive_shinies_income1
 | 
				
			||||||
 | 
					                 -->passive_shinies_income2
 | 
				
			||||||
 | 
					                 -->passive_shinies_income3
 | 
				
			||||||
							
								
								
									
										34
									
								
								pak/upgrades/instinct.mmd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								pak/upgrades/instinct.mmd
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					---
 | 
				
			||||||
 | 
					title: Instinct Upgrades
 | 
				
			||||||
 | 
					theme: base
 | 
				
			||||||
 | 
					themeVariables:
 | 
				
			||||||
 | 
					    primaryColor: "#aa00ff"
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					flowchart LR
 | 
				
			||||||
 | 
					    xp_bonus1@{label: "Stop and Smell"};
 | 
				
			||||||
 | 
					    xp_bonus2@{label: "Ponder and Deliberate"};
 | 
				
			||||||
 | 
					    xp_bonus3@{label: "Plan and Strategize"};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    theft_results1@{label: "Go For The Pockets"};
 | 
				
			||||||
 | 
					    theft_results2@{label: "Use The Winds"};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    passive_food_income1@{label: "Gone Fishin'"};
 | 
				
			||||||
 | 
					    passive_food_income2@{label: "Gone Farmin'"};
 | 
				
			||||||
 | 
					    passive_food_income3@{label: "Gone Clickin'"};
 | 
				
			||||||
 | 
					    passive_food_income4@{label: "Gone Agin'"};
 | 
				
			||||||
 | 
					    passive_food_income5@{label: "Gone Minin'"};
 | 
				
			||||||
 | 
					    passive_food_income6@{label: "Gone Factorin'"};
 | 
				
			||||||
 | 
					    passive_food_income7@{label: "Gone Bankin'"};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    passive_food_income1-->passive_food_income2
 | 
				
			||||||
 | 
					                        -->passive_food_income3
 | 
				
			||||||
 | 
					                        -->passive_food_income4
 | 
				
			||||||
 | 
					                        -->passive_food_income5
 | 
				
			||||||
 | 
					                        -->passive_food_income6
 | 
				
			||||||
 | 
					                        -->passive_food_income7;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    xp_bonus1-->xp_bonus2
 | 
				
			||||||
 | 
					             -->xp_bonus3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    theft_results1-->theft_results2
 | 
				
			||||||
 | 
					                  -->xp_bonus3;
 | 
				
			||||||
							
								
								
									
										22
									
								
								pak/upgrades/leadership.mmd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								pak/upgrades/leadership.mmd
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					---
 | 
				
			||||||
 | 
					title: Leadership Upgrades
 | 
				
			||||||
 | 
					theme: base
 | 
				
			||||||
 | 
					themeVariables:
 | 
				
			||||||
 | 
					    primaryColor: "#ffaa00"
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					flowchart LR
 | 
				
			||||||
 | 
					    offline_gen@{label: "Tireless Colony"}
 | 
				
			||||||
 | 
					    offline_gen_bonus1@{label: "Swoop Where He's Unprepared"}
 | 
				
			||||||
 | 
					    offline_gen_bonus2@{label: "Fly Where You're Unexpected"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    recruit_chance1@{label: "Squawk Softly"}
 | 
				
			||||||
 | 
					    recruit_chance2@{label: "Wink"}
 | 
				
			||||||
 | 
					    recruit_chance3@{label: "Sea Tzu's Art of Swoop"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    recruit_chance1-->recruit_chance2
 | 
				
			||||||
 | 
					    recruit_chance2-->recruit_chance3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    recruit_chance3-->offline_gen_bonus1
 | 
				
			||||||
 | 
					    offline_gen---->offline_gen_bonus1
 | 
				
			||||||
 | 
					    offline_gen_bonus1-->offline_gen_bonus2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										269
									
								
								res/doc/DoxygenLayout.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										269
									
								
								res/doc/DoxygenLayout.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,269 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<doxygenlayout version="2.0">
 | 
				
			||||||
 | 
					  <!-- Generated by doxygen 1.14.0 -->
 | 
				
			||||||
 | 
					  <!-- Navigation index tabs for HTML output -->
 | 
				
			||||||
 | 
					  <navindex>
 | 
				
			||||||
 | 
					    <tab type="mainpage" visible="yes" title=""/>
 | 
				
			||||||
 | 
					    <tab type="pages" visible="yes" title="" intro=""/>
 | 
				
			||||||
 | 
					    <tab type="topics" visible="no" title="" intro=""/>
 | 
				
			||||||
 | 
					    <tab type="modules" visible="yes" title="" intro="">
 | 
				
			||||||
 | 
					      <tab type="modulelist" visible="yes" title="" intro=""/>
 | 
				
			||||||
 | 
					      <tab type="modulemembers" visible="yes" title="" intro=""/>
 | 
				
			||||||
 | 
					    </tab>
 | 
				
			||||||
 | 
					    <tab type="namespaces" visible="yes" title="">
 | 
				
			||||||
 | 
					      <tab type="namespacelist" visible="yes" title="" intro=""/>
 | 
				
			||||||
 | 
					      <tab type="namespacemembers" visible="yes" title="" intro=""/>
 | 
				
			||||||
 | 
					    </tab>
 | 
				
			||||||
 | 
					    <tab type="concepts" visible="yes" title="">
 | 
				
			||||||
 | 
					    </tab>
 | 
				
			||||||
 | 
					    <tab type="interfaces" visible="yes" title="">
 | 
				
			||||||
 | 
					      <tab type="interfacelist" visible="yes" title="" intro=""/>
 | 
				
			||||||
 | 
					      <tab type="interfaceindex" visible="$ALPHABETICAL_INDEX" title=""/>
 | 
				
			||||||
 | 
					      <tab type="interfacehierarchy" visible="yes" title="" intro=""/>
 | 
				
			||||||
 | 
					    </tab>
 | 
				
			||||||
 | 
					    <tab type="classes" visible="yes" title="">
 | 
				
			||||||
 | 
					      <tab type="classlist" visible="yes" title="" intro=""/>
 | 
				
			||||||
 | 
					      <tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/>
 | 
				
			||||||
 | 
					      <tab type="hierarchy" visible="yes" title="" intro=""/>
 | 
				
			||||||
 | 
					      <tab type="classmembers" visible="yes" title="" intro=""/>
 | 
				
			||||||
 | 
					    </tab>
 | 
				
			||||||
 | 
					    <tab type="structs" visible="yes" title="">
 | 
				
			||||||
 | 
					      <tab type="structlist" visible="yes" title="" intro=""/>
 | 
				
			||||||
 | 
					      <tab type="structindex" visible="$ALPHABETICAL_INDEX" title=""/>
 | 
				
			||||||
 | 
					    </tab>
 | 
				
			||||||
 | 
					    <tab type="exceptions" visible="yes" title="">
 | 
				
			||||||
 | 
					      <tab type="exceptionlist" visible="yes" title="" intro=""/>
 | 
				
			||||||
 | 
					      <tab type="exceptionindex" visible="$ALPHABETICAL_INDEX" title=""/>
 | 
				
			||||||
 | 
					      <tab type="exceptionhierarchy" visible="yes" title="" intro=""/>
 | 
				
			||||||
 | 
					    </tab>
 | 
				
			||||||
 | 
					    <tab type="files" visible="yes" title="">
 | 
				
			||||||
 | 
					      <tab type="filelist" visible="yes" title="" intro=""/>
 | 
				
			||||||
 | 
					      <tab type="globals" visible="yes" title="" intro=""/>
 | 
				
			||||||
 | 
					    </tab>
 | 
				
			||||||
 | 
					    <tab type="examples" visible="yes" title="" intro=""/>
 | 
				
			||||||
 | 
					  </navindex>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <!-- Layout definition for a class page -->
 | 
				
			||||||
 | 
					  <class>
 | 
				
			||||||
 | 
					    <briefdescription visible="yes"/>
 | 
				
			||||||
 | 
					    <detaileddescription visible="yes" title=""/>
 | 
				
			||||||
 | 
					    <includes visible="$SHOW_HEADERFILE"/>
 | 
				
			||||||
 | 
					    <inheritancegraph visible="yes"/>
 | 
				
			||||||
 | 
					    <collaborationgraph visible="yes"/>
 | 
				
			||||||
 | 
					    <memberdecl>
 | 
				
			||||||
 | 
					      <nestedclasses visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <publictypes visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <services visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <interfaces visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <publicslots visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <signals visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <publicmethods visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <publicstaticmethods visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <publicattributes visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <publicstaticattributes visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <protectedtypes visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <protectedslots visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <protectedmethods visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <protectedstaticmethods visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <protectedattributes visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <protectedstaticattributes visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <packagetypes visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <packagemethods visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <packagestaticmethods visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <packageattributes visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <packagestaticattributes visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <properties visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <events visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <privatetypes visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <privateslots visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <privatemethods visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <privatestaticmethods visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <privateattributes visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <privatestaticattributes visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <friends visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <related visible="yes" title="" subtitle=""/>
 | 
				
			||||||
 | 
					      <membergroups visible="yes"/>
 | 
				
			||||||
 | 
					    </memberdecl>
 | 
				
			||||||
 | 
					    <memberdef>
 | 
				
			||||||
 | 
					      <inlineclasses visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <typedefs visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <enums visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <services visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <interfaces visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <constructors visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <functions visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <related visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <variables visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <properties visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <events visible="yes" title=""/>
 | 
				
			||||||
 | 
					    </memberdef>
 | 
				
			||||||
 | 
					    <allmemberslink visible="yes"/>
 | 
				
			||||||
 | 
					    <usedfiles visible="$SHOW_USED_FILES"/>
 | 
				
			||||||
 | 
					    <authorsection visible="yes"/>
 | 
				
			||||||
 | 
					  </class>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <!-- Layout definition for a namespace page -->
 | 
				
			||||||
 | 
					  <namespace>
 | 
				
			||||||
 | 
					    <briefdescription visible="no"/>
 | 
				
			||||||
 | 
					    <detaileddescription visible="yes" title=""/>
 | 
				
			||||||
 | 
					    <memberdecl>
 | 
				
			||||||
 | 
					      <nestednamespaces visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <constantgroups visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <interfaces visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <classes visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <concepts visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <structs visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <exceptions visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <typedefs visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <sequences visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <dictionaries visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <enums visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <functions visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <variables visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <properties visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <membergroups visible="yes"/>
 | 
				
			||||||
 | 
					    </memberdecl>
 | 
				
			||||||
 | 
					    <memberdef>
 | 
				
			||||||
 | 
					      <inlineclasses visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <typedefs visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <sequences visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <dictionaries visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <enums visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <functions visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <variables visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <properties visible="yes" title=""/>
 | 
				
			||||||
 | 
					    </memberdef>
 | 
				
			||||||
 | 
					    <authorsection visible="yes"/>
 | 
				
			||||||
 | 
					  </namespace>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <!-- Layout definition for a concept page -->
 | 
				
			||||||
 | 
					  <concept>
 | 
				
			||||||
 | 
					    <briefdescription visible="no"/>
 | 
				
			||||||
 | 
					    <detaileddescription visible="yes" title=""/>
 | 
				
			||||||
 | 
					    <includes visible="$SHOW_HEADERFILE"/>
 | 
				
			||||||
 | 
					    <definition visible="yes" title=""/>
 | 
				
			||||||
 | 
					    <authorsection visible="yes"/>
 | 
				
			||||||
 | 
					  </concept>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <!-- Layout definition for a file page -->
 | 
				
			||||||
 | 
					  <file>
 | 
				
			||||||
 | 
					    <briefdescription visible="no"/>
 | 
				
			||||||
 | 
					    <includes visible="$SHOW_INCLUDE_FILES"/>
 | 
				
			||||||
 | 
					    <includegraph visible="yes"/>
 | 
				
			||||||
 | 
					    <includedbygraph visible="yes"/>
 | 
				
			||||||
 | 
					    <sourcelink visible="yes"/>
 | 
				
			||||||
 | 
					    <memberdecl>
 | 
				
			||||||
 | 
					      <interfaces visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <classes visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <structs visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <exceptions visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <namespaces visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <concepts visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <constantgroups visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <defines visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <typedefs visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <sequences visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <dictionaries visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <enums visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <functions visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <variables visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <properties visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <membergroups visible="yes"/>
 | 
				
			||||||
 | 
					    </memberdecl>
 | 
				
			||||||
 | 
					    <detaileddescription visible="yes" title=""/>
 | 
				
			||||||
 | 
					    <memberdef>
 | 
				
			||||||
 | 
					      <inlineclasses visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <defines visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <typedefs visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <sequences visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <dictionaries visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <enums visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <functions visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <variables visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <properties visible="yes" title=""/>
 | 
				
			||||||
 | 
					    </memberdef>
 | 
				
			||||||
 | 
					    <authorsection/>
 | 
				
			||||||
 | 
					  </file>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <!-- Layout definition for a group page -->
 | 
				
			||||||
 | 
					  <group>
 | 
				
			||||||
 | 
					    <briefdescription visible="no"/>
 | 
				
			||||||
 | 
					    <detaileddescription visible="yes" title=""/>
 | 
				
			||||||
 | 
					    <groupgraph visible="yes"/>
 | 
				
			||||||
 | 
					    <memberdecl>
 | 
				
			||||||
 | 
					      <nestedgroups visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <modules visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <dirs visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <files visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <namespaces visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <concepts visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <classes visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <defines visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <typedefs visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <sequences visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <dictionaries visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <enums visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <enumvalues visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <functions visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <variables visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <signals visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <publicslots visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <protectedslots visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <privateslots visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <events visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <properties visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <friends visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <membergroups visible="yes"/>
 | 
				
			||||||
 | 
					    </memberdecl>
 | 
				
			||||||
 | 
					    <memberdef>
 | 
				
			||||||
 | 
					      <pagedocs/>
 | 
				
			||||||
 | 
					      <inlineclasses visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <defines visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <typedefs visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <sequences visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <dictionaries visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <enums visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <enumvalues visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <functions visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <variables visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <signals visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <publicslots visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <protectedslots visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <privateslots visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <events visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <properties visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <friends visible="yes" title=""/>
 | 
				
			||||||
 | 
					    </memberdef>
 | 
				
			||||||
 | 
					    <authorsection visible="yes"/>
 | 
				
			||||||
 | 
					  </group>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <!-- Layout definition for a C++20 module page -->
 | 
				
			||||||
 | 
					  <module>
 | 
				
			||||||
 | 
					    <briefdescription visible="no"/>
 | 
				
			||||||
 | 
					    <detaileddescription visible="yes" title=""/>
 | 
				
			||||||
 | 
					    <exportedmodules visible="yes"/>
 | 
				
			||||||
 | 
					    <memberdecl>
 | 
				
			||||||
 | 
					      <concepts visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <classes visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <enums visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <typedefs visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <functions visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <variables visible="yes" title=""/>
 | 
				
			||||||
 | 
					      <membergroups visible="yes" title=""/>
 | 
				
			||||||
 | 
					    </memberdecl>
 | 
				
			||||||
 | 
					    <memberdecl>
 | 
				
			||||||
 | 
					      <files visible="yes"/>
 | 
				
			||||||
 | 
					    </memberdecl>
 | 
				
			||||||
 | 
					  </module>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <!-- Layout definition for a directory page -->
 | 
				
			||||||
 | 
					  <directory>
 | 
				
			||||||
 | 
					    <briefdescription visible="no"/>
 | 
				
			||||||
 | 
					    <directorygraph visible="yes"/>
 | 
				
			||||||
 | 
					    <memberdecl>
 | 
				
			||||||
 | 
					      <dirs visible="yes"/>
 | 
				
			||||||
 | 
					      <files visible="yes"/>
 | 
				
			||||||
 | 
					    </memberdecl>
 | 
				
			||||||
 | 
					    <detaileddescription visible="yes" title=""/>
 | 
				
			||||||
 | 
					  </directory>
 | 
				
			||||||
 | 
					</doxygenlayout>
 | 
				
			||||||
							
								
								
									
										18
									
								
								res/doc/footer.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								res/doc/footer.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					<!-- HTML footer for doxygen 1.14.0-->
 | 
				
			||||||
 | 
					<!-- start footer part -->
 | 
				
			||||||
 | 
					<!--BEGIN GENERATE_TREEVIEW-->
 | 
				
			||||||
 | 
					<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
 | 
				
			||||||
 | 
					  <ul>
 | 
				
			||||||
 | 
					    $navpath
 | 
				
			||||||
 | 
					    <li class="footer">$generatedby <a href="https://www.doxygen.org/index.html"><img class="footer" src="$relpath^doxygen.svg" width="104" height="31" alt="doxygen"/></a> $doxygenversion </li>
 | 
				
			||||||
 | 
					  </ul>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					<!--END GENERATE_TREEVIEW-->
 | 
				
			||||||
 | 
					<!--BEGIN !GENERATE_TREEVIEW-->
 | 
				
			||||||
 | 
					<hr class="footer"/><address class="footer"><small>
 | 
				
			||||||
 | 
					$generatedby <a href="https://www.doxygen.org/index.html"><img class="footer" src="$relpath^doxygen.svg" width="104" height="31" alt="doxygen"/></a> $doxygenversion
 | 
				
			||||||
 | 
					</small></address>
 | 
				
			||||||
 | 
					</div><!-- doc-content -->
 | 
				
			||||||
 | 
					<!--END !GENERATE_TREEVIEW-->
 | 
				
			||||||
 | 
					</body>
 | 
				
			||||||
 | 
					</html>
 | 
				
			||||||
							
								
								
									
										76
									
								
								res/doc/header.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								res/doc/header.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,76 @@
 | 
				
			|||||||
 | 
					<!-- HTML header for doxygen 1.14.0-->
 | 
				
			||||||
 | 
					<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 | 
				
			||||||
 | 
					<html xmlns="http://www.w3.org/1999/xhtml" lang="$langISO" class="light-mode">
 | 
				
			||||||
 | 
					<head>
 | 
				
			||||||
 | 
					<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
 | 
				
			||||||
 | 
					<meta http-equiv="X-UA-Compatible" content="IE=11"/>
 | 
				
			||||||
 | 
					<meta name="generator" content="Doxygen $doxygenversion"/>
 | 
				
			||||||
 | 
					<meta name="viewport" content="width=device-width, initial-scale=1"/>
 | 
				
			||||||
 | 
					<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
 | 
				
			||||||
 | 
					<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
 | 
				
			||||||
 | 
					<!--BEGIN PROJECT_ICON-->
 | 
				
			||||||
 | 
					<link rel="icon" href="$relpath^$projecticon" type="image/x-icon" />
 | 
				
			||||||
 | 
					<!--END PROJECT_ICON-->
 | 
				
			||||||
 | 
					<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
 | 
				
			||||||
 | 
					<!--BEGIN FULL_SIDEBAR-->
 | 
				
			||||||
 | 
					<script type="text/javascript">var page_layout=1;</script>
 | 
				
			||||||
 | 
					<!--END FULL_SIDEBAR-->
 | 
				
			||||||
 | 
					<script type="text/javascript" src="$relpath^jquery.js"></script>
 | 
				
			||||||
 | 
					<script type="text/javascript" src="$relpath^dynsections.js"></script>
 | 
				
			||||||
 | 
					<!--BEGIN COPY_CLIPBOARD-->
 | 
				
			||||||
 | 
					<script type="text/javascript" src="$relpath^clipboard.js"></script>
 | 
				
			||||||
 | 
					<!--END COPY_CLIPBOARD-->
 | 
				
			||||||
 | 
					$treeview
 | 
				
			||||||
 | 
					$search
 | 
				
			||||||
 | 
					$mathjax
 | 
				
			||||||
 | 
					$darkmode
 | 
				
			||||||
 | 
					<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
 | 
				
			||||||
 | 
					$extrastylesheet
 | 
				
			||||||
 | 
					</head>
 | 
				
			||||||
 | 
					<body>
 | 
				
			||||||
 | 
					<!--BEGIN FULL_SIDEBAR-->
 | 
				
			||||||
 | 
					<div id="side-nav" class="ui-resizable side-nav-resizable"><!-- do not remove this div, it is closed by doxygen! -->
 | 
				
			||||||
 | 
					<!--END FULL_SIDEBAR-->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!--BEGIN TITLEAREA-->
 | 
				
			||||||
 | 
					<div id="titlearea">
 | 
				
			||||||
 | 
					<table cellspacing="0" cellpadding="0">
 | 
				
			||||||
 | 
					 <tbody>
 | 
				
			||||||
 | 
					 <tr id="projectrow">
 | 
				
			||||||
 | 
					  <!--BEGIN PROJECT_LOGO-->
 | 
				
			||||||
 | 
					  <td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo"$logosize/></td>
 | 
				
			||||||
 | 
					  <!--END PROJECT_LOGO-->
 | 
				
			||||||
 | 
					  <!--BEGIN PROJECT_NAME-->
 | 
				
			||||||
 | 
					  <td id="projectalign">
 | 
				
			||||||
 | 
					   <div id="projectname">$projectname<!--BEGIN PROJECT_NUMBER--><span id="projectnumber"> $projectnumber</span><!--END PROJECT_NUMBER-->
 | 
				
			||||||
 | 
					   </div>
 | 
				
			||||||
 | 
					   <!--BEGIN PROJECT_BRIEF--><div id="projectbrief">$projectbrief</div><!--END PROJECT_BRIEF-->
 | 
				
			||||||
 | 
					  </td>
 | 
				
			||||||
 | 
					  <!--END PROJECT_NAME-->
 | 
				
			||||||
 | 
					  <!--BEGIN !PROJECT_NAME-->
 | 
				
			||||||
 | 
					   <!--BEGIN PROJECT_BRIEF-->
 | 
				
			||||||
 | 
					    <td>
 | 
				
			||||||
 | 
					    <div id="projectbrief">$projectbrief</div>
 | 
				
			||||||
 | 
					    </td>
 | 
				
			||||||
 | 
					   <!--END PROJECT_BRIEF-->
 | 
				
			||||||
 | 
					  <!--END !PROJECT_NAME-->
 | 
				
			||||||
 | 
					  <!--BEGIN DISABLE_INDEX-->
 | 
				
			||||||
 | 
					   <!--BEGIN SEARCHENGINE-->
 | 
				
			||||||
 | 
					     <!--BEGIN !FULL_SIDEBAR-->
 | 
				
			||||||
 | 
					    <td>$searchbox</td>
 | 
				
			||||||
 | 
					     <!--END !FULL_SIDEBAR-->
 | 
				
			||||||
 | 
					   <!--END SEARCHENGINE-->
 | 
				
			||||||
 | 
					  <!--END DISABLE_INDEX-->
 | 
				
			||||||
 | 
					 </tr>
 | 
				
			||||||
 | 
					  <!--BEGIN SEARCHENGINE-->
 | 
				
			||||||
 | 
					   <!--BEGIN FULL_SIDEBAR-->
 | 
				
			||||||
 | 
					   <tr><td colspan="2">$searchbox</td></tr>
 | 
				
			||||||
 | 
					   <!--END FULL_SIDEBAR-->
 | 
				
			||||||
 | 
					  <!--END SEARCHENGINE-->
 | 
				
			||||||
 | 
					 </tbody>
 | 
				
			||||||
 | 
					</table>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					<!--END TITLEAREA-->
 | 
				
			||||||
 | 
					<!-- end header part -->
 | 
				
			||||||
							
								
								
									
										6
									
								
								res/doc/index.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								res/doc/index.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					Seagull Game                                {#mainpage}
 | 
				
			||||||
 | 
					============
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This is a game about a seagull.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The rest is up to you.
 | 
				
			||||||
							
								
								
									
										11
									
								
								res/doc/play/index.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								res/doc/play/index.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					How to Play                             {#play}
 | 
				
			||||||
 | 
					===========
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					How to play the Seagull Game:
 | 
				
			||||||
 | 
					* Steal food and shinies.
 | 
				
			||||||
 | 
					* That's it.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Subpages
 | 
				
			||||||
 | 
					* \subpage play-upgrades
 | 
				
			||||||
 | 
					* \subpage math
 | 
				
			||||||
							
								
								
									
										15
									
								
								res/doc/play/math.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								res/doc/play/math.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					Mathematical Calculations Used In Game          {#math}
 | 
				
			||||||
 | 
					======================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For more information on how dice rolls are calculated, see actions.dice_roll()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Calculated Attributes
 | 
				
			||||||
 | 
					Attribute | Formula
 | 
				
			||||||
 | 
					----------|--------
 | 
				
			||||||
 | 
					Speed     | \f(\LARGE\(3+(AGL * 1.5)\)\f)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Action Success Chances
 | 
				
			||||||
 | 
					Action             | Chance Calculation
 | 
				
			||||||
 | 
					-------------------|-------------------
 | 
				
			||||||
 | 
					Stealing resources | \f(\LARGE\((roll+speed)+(\frac{AGL}{4}) \geq 50\)\f)
 | 
				
			||||||
 | 
					Recruiting         | \f(\LARGE\(roll+(LDR*\frac{R_{shinies}}{Colony}) \geq 65\)\f)
 | 
				
			||||||
							
								
								
									
										4
									
								
								res/doc/play/upgrades.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								res/doc/play/upgrades.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					Upgrades                            {#play-upgrades}
 | 
				
			||||||
 | 
					========
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You can buy upgrades in this game!
 | 
				
			||||||
							
								
								
									
										3
									
								
								res/doc/python/actions.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								res/doc/python/actions.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					\namespace actions
 | 
				
			||||||
 | 
					\brief Defines game logic functions for in-game actions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										2
									
								
								res/doc/python/core.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								res/doc/python/core.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					\namespace core
 | 
				
			||||||
 | 
					\brief Bare minimum needed for a Seagull-game-like environment.
 | 
				
			||||||
							
								
								
									
										5
									
								
								res/doc/python/desktop.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								res/doc/python/desktop.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					\namespace desktop
 | 
				
			||||||
 | 
					\brief Implementation code for desktop specific needs.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This module also includes a rudimentary "local storage" system within JS_API, since pywebview's local storage APIs
 | 
				
			||||||
 | 
					seemed non-functional last time I tried to use them.
 | 
				
			||||||
							
								
								
									
										3
									
								
								res/doc/python/gamedata.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								res/doc/python/gamedata.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					\namespace gamedata
 | 
				
			||||||
 | 
					\internal
 | 
				
			||||||
 | 
					\brief Contains everything related to the virtual filesystem.
 | 
				
			||||||
							
								
								
									
										2
									
								
								res/doc/python/items.generate_item_description.mdpart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								res/doc/python/items.generate_item_description.mdpart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					This uses <a href="https://rant-lang.org">Rant</a>, a procedural generation language, along with a compiled in
 | 
				
			||||||
 | 
					wordlist module from a third party wordlist, to generate a unique and occasionally silly item description. 
 | 
				
			||||||
							
								
								
									
										9
									
								
								res/doc/python/items.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								res/doc/python/items.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					\namespace items
 | 
				
			||||||
 | 
					\brief Game logic related to items and resources.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This module handles generating of items, each worth a given amount of resources. The values are first calculated
 | 
				
			||||||
 | 
					according to game rules defined in XML files, then any given modifiers are applied.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					\internal
 | 
				
			||||||
 | 
					\note This module is almost fully VFS aware.
 | 
				
			||||||
 | 
					\endinternal
 | 
				
			||||||
							
								
								
									
										2
									
								
								res/doc/python/tick.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								res/doc/python/tick.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					\namespace tick
 | 
				
			||||||
 | 
					\brief All logic related to game ticks.
 | 
				
			||||||
							
								
								
									
										2
									
								
								res/doc/python/upgrades.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								res/doc/python/upgrades.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					\namespace upgrades
 | 
				
			||||||
 | 
					\brief Upgrade tree related functions.
 | 
				
			||||||
							
								
								
									
										224
									
								
								res/doc/style.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								res/doc/style.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,224 @@
 | 
				
			|||||||
 | 
					html {
 | 
				
			||||||
 | 
					    --primary-color: #17b9c4;
 | 
				
			||||||
 | 
					    --primary-dark-color: #337d80;
 | 
				
			||||||
 | 
					    --primary-light-color: #70d9e9;
 | 
				
			||||||
 | 
					    --on-primary-color: #ffffff;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* page base colors */
 | 
				
			||||||
 | 
					    --page-background-color: #ffffff;
 | 
				
			||||||
 | 
					    --page-foreground-color: #2f4153;
 | 
				
			||||||
 | 
					    --page-secondary-foreground-color: #6f7e8e;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* color for all separators on the website: hr, borders, ... */
 | 
				
			||||||
 | 
					    --separator-color: #dedede;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* border radius for all rounded components. Will affect many components, like dropdowns, memitems, codeblocks, ... */
 | 
				
			||||||
 | 
					    --border-radius-large: 8px;
 | 
				
			||||||
 | 
					    --border-radius-small: 4px;
 | 
				
			||||||
 | 
					    --border-radius-medium: 6px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* default spacings. Most components reference these values for spacing, to provide uniform spacing on the page. */
 | 
				
			||||||
 | 
					    --spacing-small: 5px;
 | 
				
			||||||
 | 
					    --spacing-medium: 10px;
 | 
				
			||||||
 | 
					    --spacing-large: 16px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* default box shadow used for raising an element above the normal content. Used in dropdowns, search result, ... */
 | 
				
			||||||
 | 
					    --box-shadow: 0 2px 8px 0 rgba(0,0,0,.075);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    --odd-color: rgba(0,0,0,.028);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* font-families. will affect all text on the website
 | 
				
			||||||
 | 
					     * font-family: the normal font for text, headlines, menus
 | 
				
			||||||
 | 
					     * font-family-monospace: used for preformatted text in memtitle, code, fragments
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    --font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;
 | 
				
			||||||
 | 
					    --font-family-monospace: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* font sizes */
 | 
				
			||||||
 | 
					    --page-font-size: 15.6px;
 | 
				
			||||||
 | 
					    --navigation-font-size: 14.4px;
 | 
				
			||||||
 | 
					    --toc-font-size: 13.4px;
 | 
				
			||||||
 | 
					    --code-font-size: 14px; /* affects code, fragment */
 | 
				
			||||||
 | 
					    --title-font-size: 22px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* content text properties. These only affect the page content, not the navigation or any other ui elements */
 | 
				
			||||||
 | 
					    --content-line-height: 27px;
 | 
				
			||||||
 | 
					    /* The content is centered and constraint in it's width. To make the content fill the whole page, set the variable to auto.*/
 | 
				
			||||||
 | 
					    --content-maxwidth: 1050px;
 | 
				
			||||||
 | 
					    --table-line-height: 24px;
 | 
				
			||||||
 | 
					    --toc-sticky-top: var(--spacing-medium);
 | 
				
			||||||
 | 
					    --toc-width: 200px;
 | 
				
			||||||
 | 
					    --toc-max-height: calc(100vh - 2 * var(--spacing-medium) - 85px);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* colors for various content boxes: @warning, @note, @deprecated @bug */
 | 
				
			||||||
 | 
					    --warning-color: #faf3d8;
 | 
				
			||||||
 | 
					    --warning-color-dark: #f3a600;
 | 
				
			||||||
 | 
					    --warning-color-darker: #5f4204;
 | 
				
			||||||
 | 
					    --note-color: #e4f3ff;
 | 
				
			||||||
 | 
					    --note-color-dark: #1879C4;
 | 
				
			||||||
 | 
					    --note-color-darker: #274a5c;
 | 
				
			||||||
 | 
					    --todo-color: #e4dafd;
 | 
				
			||||||
 | 
					    --todo-color-dark: #5b2bdd;
 | 
				
			||||||
 | 
					    --todo-color-darker: #2a0d72;
 | 
				
			||||||
 | 
					    --deprecated-color: #ecf0f3;
 | 
				
			||||||
 | 
					    --deprecated-color-dark: #5b6269;
 | 
				
			||||||
 | 
					    --deprecated-color-darker: #43454a;
 | 
				
			||||||
 | 
					    --bug-color: #f8d1cc;
 | 
				
			||||||
 | 
					    --bug-color-dark: #b61825;
 | 
				
			||||||
 | 
					    --bug-color-darker: #75070f;
 | 
				
			||||||
 | 
					    --invariant-color: #d8f1e3;
 | 
				
			||||||
 | 
					    --invariant-color-dark: #44b86f;
 | 
				
			||||||
 | 
					    --invariant-color-darker: #265532;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* blockquote colors */
 | 
				
			||||||
 | 
					    --blockquote-background: #f8f9fa;
 | 
				
			||||||
 | 
					    --blockquote-foreground: #636568;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* table colors */
 | 
				
			||||||
 | 
					    --tablehead-background: #f1f1f1;
 | 
				
			||||||
 | 
					    --tablehead-foreground: var(--page-foreground-color);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* menu-display: block | none
 | 
				
			||||||
 | 
					     * Visibility of the top navigation on screens >= 768px. On smaller screen the menu is always visible.
 | 
				
			||||||
 | 
					     * `GENERATE_TREEVIEW` MUST be enabled!
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    --menu-display: block;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    --menu-focus-foreground: var(--on-primary-color);
 | 
				
			||||||
 | 
					    --menu-focus-background: var(--primary-color);
 | 
				
			||||||
 | 
					    --menu-selected-background: rgba(0,0,0,.05);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    --header-background: var(--page-background-color);
 | 
				
			||||||
 | 
					    --header-foreground: var(--page-foreground-color);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* searchbar colors */
 | 
				
			||||||
 | 
					    --searchbar-background: var(--side-nav-background);
 | 
				
			||||||
 | 
					    --searchbar-foreground: var(--page-foreground-color);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* searchbar size
 | 
				
			||||||
 | 
					     * (`searchbar-width` is only applied on screens >= 768px.
 | 
				
			||||||
 | 
					     * on smaller screens the searchbar will always fill the entire screen width) */
 | 
				
			||||||
 | 
					    --searchbar-height: 33px;
 | 
				
			||||||
 | 
					    --searchbar-width: 210px;
 | 
				
			||||||
 | 
					    --searchbar-border-radius: var(--searchbar-height);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* code block colors */
 | 
				
			||||||
 | 
					    --code-background: #f5f5f5;
 | 
				
			||||||
 | 
					    --code-foreground: var(--page-foreground-color);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* fragment colors */
 | 
				
			||||||
 | 
					    --fragment-background: #F8F9FA;
 | 
				
			||||||
 | 
					    --fragment-foreground: #37474F;
 | 
				
			||||||
 | 
					    --fragment-keyword: #bb6bb2;
 | 
				
			||||||
 | 
					    --fragment-keywordtype: #8258b3;
 | 
				
			||||||
 | 
					    --fragment-keywordflow: #d67c3b;
 | 
				
			||||||
 | 
					    --fragment-token: #438a59;
 | 
				
			||||||
 | 
					    --fragment-comment: #969696;
 | 
				
			||||||
 | 
					    --fragment-link: #5383d6;
 | 
				
			||||||
 | 
					    --fragment-preprocessor: #46aaa5;
 | 
				
			||||||
 | 
					    --fragment-linenumber-color: #797979;
 | 
				
			||||||
 | 
					    --fragment-linenumber-background: #f4f4f5;
 | 
				
			||||||
 | 
					    --fragment-linenumber-border: #e3e5e7;
 | 
				
			||||||
 | 
					    --fragment-lineheight: 20px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* sidebar navigation (treeview) colors */
 | 
				
			||||||
 | 
					    --side-nav-background: #fbfbfb;
 | 
				
			||||||
 | 
					    --side-nav-foreground: var(--page-foreground-color);
 | 
				
			||||||
 | 
					    --side-nav-arrow-opacity: 0;
 | 
				
			||||||
 | 
					    --side-nav-arrow-hover-opacity: 0.9;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    --toc-background: var(--side-nav-background);
 | 
				
			||||||
 | 
					    --toc-foreground: var(--side-nav-foreground);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* height of an item in any tree / collapsible table */
 | 
				
			||||||
 | 
					    --tree-item-height: 30px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    --memname-font-size: var(--code-font-size);
 | 
				
			||||||
 | 
					    --memtitle-font-size: 18px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    --webkit-scrollbar-size: 7px;
 | 
				
			||||||
 | 
					    --webkit-scrollbar-padding: 4px;
 | 
				
			||||||
 | 
					    --webkit-scrollbar-color: var(--separator-color);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    --animation-duration: .12s
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					span.api-method {
 | 
				
			||||||
 | 
					    padding: 3px;
 | 
				
			||||||
 | 
					    font-weight: bold;
 | 
				
			||||||
 | 
					    border-radius: 5px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					span.api-method-GET {
 | 
				
			||||||
 | 
					    background-color: #c4eecf;
 | 
				
			||||||
 | 
					    color: #10491f;
 | 
				
			||||||
 | 
					    border: 1px #48d96f dashed;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					span.api-method-POST {
 | 
				
			||||||
 | 
					    background-color: #eee3c4;
 | 
				
			||||||
 | 
					    color: #493e10;
 | 
				
			||||||
 | 
					    border: 1px #d9bc48 dashed;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@media (prefers-color-scheme: dark) {
 | 
				
			||||||
 | 
					    html:not(.light-mode) {
 | 
				
			||||||
 | 
					        color-scheme: dark;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        --primary-color: #19c9d2;
 | 
				
			||||||
 | 
					        --primary-dark-color: #86c0c4;
 | 
				
			||||||
 | 
					        --primary-light-color: #478eac;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        --box-shadow: 0 2px 8px 0 rgba(0,0,0,.35);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        --odd-color: rgba(100,100,100,.06);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        --menu-selected-background: rgba(0,0,0,.4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        --page-background-color: #1C1D1F;
 | 
				
			||||||
 | 
					        --page-foreground-color: #d2dbde;
 | 
				
			||||||
 | 
					        --page-secondary-foreground-color: #859399;
 | 
				
			||||||
 | 
					        --separator-color: #38393b;
 | 
				
			||||||
 | 
					        --side-nav-background: #252628;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        --code-background: #2a2c2f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        --tablehead-background: #2a2c2f;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					        --blockquote-background: #222325;
 | 
				
			||||||
 | 
					        --blockquote-foreground: #7e8c92;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        --warning-color: #3b2e04;
 | 
				
			||||||
 | 
					        --warning-color-dark: #f1b602;
 | 
				
			||||||
 | 
					        --warning-color-darker: #ceb670;
 | 
				
			||||||
 | 
					        --note-color: #163750;
 | 
				
			||||||
 | 
					        --note-color-dark: #1982D2;
 | 
				
			||||||
 | 
					        --note-color-darker: #dcf0fa;
 | 
				
			||||||
 | 
					        --todo-color: #2a2536;
 | 
				
			||||||
 | 
					        --todo-color-dark: #7661b3;
 | 
				
			||||||
 | 
					        --todo-color-darker: #ae9ed6;
 | 
				
			||||||
 | 
					        --deprecated-color: #2e323b;
 | 
				
			||||||
 | 
					        --deprecated-color-dark: #738396;
 | 
				
			||||||
 | 
					        --deprecated-color-darker: #abb0bd;
 | 
				
			||||||
 | 
					        --bug-color: #2e1917;
 | 
				
			||||||
 | 
					        --bug-color-dark: #ad2617;
 | 
				
			||||||
 | 
					        --bug-color-darker: #f5b1aa;
 | 
				
			||||||
 | 
					        --invariant-color: #303a35;
 | 
				
			||||||
 | 
					        --invariant-color-dark: #76ce96;
 | 
				
			||||||
 | 
					        --invariant-color-darker: #cceed5;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        --fragment-background: #282c34;
 | 
				
			||||||
 | 
					        --fragment-foreground: #dbe4eb;
 | 
				
			||||||
 | 
					        --fragment-keyword: #cc99cd;
 | 
				
			||||||
 | 
					        --fragment-keywordtype: #ab99cd;
 | 
				
			||||||
 | 
					        --fragment-keywordflow: #e08000;
 | 
				
			||||||
 | 
					        --fragment-token: #7ec699;
 | 
				
			||||||
 | 
					        --fragment-comment: #999999;
 | 
				
			||||||
 | 
					        --fragment-link: #98c0e3;
 | 
				
			||||||
 | 
					        --fragment-preprocessor: #65cabe;
 | 
				
			||||||
 | 
					        --fragment-linenumber-color: #cccccc;
 | 
				
			||||||
 | 
					        --fragment-linenumber-background: #35393c;
 | 
				
			||||||
 | 
					        --fragment-linenumber-border: #1f1f1f;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -2,12 +2,12 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
a = Analysis(
 | 
					a = Analysis(
 | 
				
			||||||
    ['app/desktop.py'],
 | 
					    ['@CMAKE_SOURCE_DIR@/app/desktop.py'],
 | 
				
			||||||
    pathex=[],
 | 
					    pathex=[],
 | 
				
			||||||
    binaries=[],
 | 
					    binaries=[],
 | 
				
			||||||
    datas=[
 | 
					    datas=[
 | 
				
			||||||
        ('app/rant', 'basepak/rant'),
 | 
					        ('@CMAKE_SOURCE_DIR@/app/basepak', 'basepak'),
 | 
				
			||||||
        ('app/rules/schemas', 'basepak/rules/schemas'),
 | 
					        ('@CMAKE_CURRENT_BINARY_DIR@/basepak', 'basepak'),
 | 
				
			||||||
        ('opt', './opt')
 | 
					        ('opt', './opt')
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    hiddenimports=[],
 | 
					    hiddenimports=[],
 | 
				
			||||||
@@ -20,8 +20,14 @@ a = Analysis(
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
pyz = PYZ(a.pure)
 | 
					pyz = PYZ(a.pure)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					splash = Splash("@CMAKE_SOURCE_DIR@/pak/static/image/splash.png",
 | 
				
			||||||
 | 
					                binaries=a.binaries, datas=a.datas,
 | 
				
			||||||
 | 
					                text_pos=(0,0), text_size=12, text_color="white")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
exe = EXE(
 | 
					exe = EXE(
 | 
				
			||||||
    pyz,
 | 
					    pyz,
 | 
				
			||||||
 | 
					    splash,
 | 
				
			||||||
 | 
					    splash.binaries,
 | 
				
			||||||
    a.scripts,
 | 
					    a.scripts,
 | 
				
			||||||
    a.binaries,
 | 
					    a.binaries,
 | 
				
			||||||
    a.datas,
 | 
					    a.datas,
 | 
				
			||||||
		Reference in New Issue
	
	Block a user