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_cache/**
 | 
			
		||||
 | 
			
		||||
# autogenerated
 | 
			
		||||
doc/**
 | 
			
		||||
 | 
			
		||||
**/__pycache__/**
 | 
			
		||||
**.pyc
 | 
			
		||||
							
								
								
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +1,6 @@
 | 
			
		||||
[submodule "ext/imsky/wordlists"]
 | 
			
		||||
	path = ext/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/**
 | 
			
		||||
ext/imsky/wordlists/**
 | 
			
		||||
static/image/splash.png
 | 
			
		||||
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"?>
 | 
			
		||||
<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: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:complexType>
 | 
			
		||||
            <xs:choice maxOccurs="unbounded">
 | 
			
		||||
@@ -23,6 +31,13 @@
 | 
			
		||||
                  </xs:sequence>
 | 
			
		||||
                </xs:complexType>
 | 
			
		||||
              </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:complexType>
 | 
			
		||||
        </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
 | 
			
		||||
 | 
			
		||||
## \file desktop.py
 | 
			
		||||
#  \brief Entrypoint file for desktop versions of the Seagull Game.
 | 
			
		||||
 | 
			
		||||
import argparse
 | 
			
		||||
import os
 | 
			
		||||
import pathlib
 | 
			
		||||
@@ -9,8 +12,19 @@ import webview
 | 
			
		||||
 | 
			
		||||
import flask
 | 
			
		||||
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
 | 
			
		||||
sig_exit = threading.Event()
 | 
			
		||||
@@ -23,13 +37,14 @@ argo = argp.parse_args()
 | 
			
		||||
def index():
 | 
			
		||||
    if not core.base_context_live:
 | 
			
		||||
        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(1, core.app.url_for("static", filename="js/seagull-desktop.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"), False))
 | 
			
		||||
    gamedata.vfs.copy_out("templates/main_page.j2", dest=core.path_appdir.as_posix())
 | 
			
		||||
    return flask.render_template("main_page.j2", **core.base_context)
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    try:
 | 
			
		||||
        pyi_splash.update_text("Determining storage directory")
 | 
			
		||||
        if sys.platform.startswith("win"):
 | 
			
		||||
            storage_dir = pathlib.Path(os.environ["APPDATA"]) / "seagull"
 | 
			
		||||
        elif sys.platform.startswith("darwin"): # macos
 | 
			
		||||
@@ -39,13 +54,16 @@ if __name__ == "__main__":
 | 
			
		||||
            storage_dir = pathlib.Path(os.environ["HOME"]) / ".local/share/seagull"
 | 
			
		||||
        desktop.path_storagedir = storage_dir
 | 
			
		||||
 | 
			
		||||
        pyi_splash.update_text("Loading VFS")
 | 
			
		||||
        gamedata.vfs.load_data_source("basepak")
 | 
			
		||||
        gamedata.vfs.load_data_source("seagull.pak", proto="zip")
 | 
			
		||||
 | 
			
		||||
        if argo.debug:
 | 
			
		||||
            desktop.api.debug_mode = True
 | 
			
		||||
        pyi_splash.update_text("Starting browser shell")
 | 
			
		||||
        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)
 | 
			
		||||
    except KeyboardInterrupt:
 | 
			
		||||
        core.log.info("Goodnight, moon ...")
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,15 @@ import flask
 | 
			
		||||
 | 
			
		||||
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=[]):
 | 
			
		||||
    result = random.randint(min, max)
 | 
			
		||||
    for _, mod in modifiers:
 | 
			
		||||
@@ -12,12 +21,29 @@ def dice_roll(min=0, max=100, modifiers=[]):
 | 
			
		||||
    
 | 
			
		||||
    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"])
 | 
			
		||||
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({
 | 
			
		||||
        "success": (dice_roll() >= 50)
 | 
			
		||||
        "success": (result >= 50)
 | 
			
		||||
    }), 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"])
 | 
			
		||||
def recruit():
 | 
			
		||||
    return flask.Response(json.dumps({
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ import pathlib
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
import flask
 | 
			
		||||
from flask_cors import CORS
 | 
			
		||||
 | 
			
		||||
log = logging.getLogger()
 | 
			
		||||
pipe_stderr = logging.StreamHandler(sys.stderr)
 | 
			
		||||
@@ -16,11 +17,16 @@ else:
 | 
			
		||||
    path_appdir = pathlib.Path.cwd()
 | 
			
		||||
log.critical(path_appdir)
 | 
			
		||||
 | 
			
		||||
## \internal
 | 
			
		||||
#  \brief Signals whether we are a desktop application (as opposed to a Docker container).
 | 
			
		||||
desktop_mode = False
 | 
			
		||||
 | 
			
		||||
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")
 | 
			
		||||
CORS(app)
 | 
			
		||||
orig_url_for = app.url_for
 | 
			
		||||
 | 
			
		||||
xml_namespaces = {
 | 
			
		||||
@@ -34,6 +40,10 @@ xml_namespaces = {
 | 
			
		||||
#REDIS_PASS="i am not a real password"
 | 
			
		||||
#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):
 | 
			
		||||
    if endpoint == "static":
 | 
			
		||||
        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
 | 
			
		||||
 | 
			
		||||
## \internal
 | 
			
		||||
#  \brief Base Flask rendering context. Generated with render_base_context().
 | 
			
		||||
base_context = {}
 | 
			
		||||
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>")
 | 
			
		||||
def render_dialog(dialog):
 | 
			
		||||
    if gamedata.vfs.exists(f"templates/{dialog}.j2"):
 | 
			
		||||
        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")
 | 
			
		||||
    else:
 | 
			
		||||
        return "", 404
 | 
			
		||||
 | 
			
		||||
## \brief Prepares the base rendering context for Flask to serve our content.
 | 
			
		||||
def render_base_context():
 | 
			
		||||
    global base_context
 | 
			
		||||
    global base_context_live
 | 
			
		||||
@@ -68,19 +86,32 @@ def render_base_context():
 | 
			
		||||
    domain_components = flask.request.host.split(".")
 | 
			
		||||
    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...
 | 
			
		||||
    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["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"
 | 
			
		||||
    else:                            # dev, serve files from here
 | 
			
		||||
        #print(base_domain)
 | 
			
		||||
        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_live = True
 | 
			
		||||
 | 
			
		||||
## \brief Returns OK. Useful for health checks.
 | 
			
		||||
#  \api{GET} /core/ping
 | 
			
		||||
@app.route("/core/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")
 | 
			
		||||
@@ -2,14 +2,29 @@ import pathlib
 | 
			
		||||
 | 
			
		||||
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()
 | 
			
		||||
 | 
			
		||||
## \internal
 | 
			
		||||
#  \brief Defines JavaScript API functions for pywebview.
 | 
			
		||||
class JS_API:
 | 
			
		||||
    #  \brief Whether or not we're in debug mode.
 | 
			
		||||
    debug_mode = False
 | 
			
		||||
 | 
			
		||||
    def __init__(self, debug_mode=False):
 | 
			
		||||
        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):
 | 
			
		||||
        if not (path_storagedir / key).exists():
 | 
			
		||||
            return None
 | 
			
		||||
@@ -21,6 +36,11 @@ class JS_API:
 | 
			
		||||
                core.log.error(f"problem loading {key} (from {path_storagedir}): {exc}")
 | 
			
		||||
                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):
 | 
			
		||||
        with open(path_storagedir / key, "w") as fd_datafile:
 | 
			
		||||
            try:
 | 
			
		||||
@@ -28,6 +48,8 @@ class JS_API:
 | 
			
		||||
            except Exception as 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):
 | 
			
		||||
        if (path_storagedir / key).exists():
 | 
			
		||||
            (path_storagedir / key).unlink()
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,11 @@
 | 
			
		||||
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")
 | 
			
		||||
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)
 | 
			
		||||
@@ -12,6 +12,8 @@ import fs.osfs
 | 
			
		||||
 | 
			
		||||
from . import core
 | 
			
		||||
 | 
			
		||||
## \brief Handler for virtual filesystem.
 | 
			
		||||
#  \internal
 | 
			
		||||
class GameVFSHandler(object):
 | 
			
		||||
    vfs = None
 | 
			
		||||
    log = logging.getLogger().getChild("vfs")
 | 
			
		||||
@@ -43,6 +45,10 @@ class GameVFSHandler(object):
 | 
			
		||||
        except:
 | 
			
		||||
            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"):
 | 
			
		||||
        print(f"data source: {source} ({proto})")
 | 
			
		||||
        assert self.vfs is not None
 | 
			
		||||
@@ -63,6 +69,10 @@ class GameVFSHandler(object):
 | 
			
		||||
            self.log.info(f"loading vfs source: {fs_source} (pyfilesystem2 handler)")
 | 
			
		||||
            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):
 | 
			
		||||
        if not dest:
 | 
			
		||||
            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)
 | 
			
		||||
            return (pth_dest / filepath).as_posix()
 | 
			
		||||
 | 
			
		||||
## \brief Primary VFS handler.
 | 
			
		||||
#  \internal
 | 
			
		||||
vfs = GameVFSHandler()
 | 
			
		||||
@@ -10,6 +10,8 @@ valid_resources = [
 | 
			
		||||
    "food", "shinies", "psi" # early game
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
## \internal
 | 
			
		||||
#  \brief The environment variable map to run Rant with.
 | 
			
		||||
rant_env = os.environ.copy()
 | 
			
		||||
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_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):
 | 
			
		||||
    if core.desktop_mode:
 | 
			
		||||
        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())
 | 
			
		||||
    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):
 | 
			
		||||
    count = random.randint(min, max)
 | 
			
		||||
    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]
 | 
			
		||||
        resource_rules.append((res_rule, int(mindata.text), int(maxdata.text)))
 | 
			
		||||
    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]}")
 | 
			
		||||
        core.log.warning(f"{resource} vs humans: {resource_rules[0][1]} <-> {resource_rules[0][2]} (sb: {resource_rules[0][0].get("StoryBeat", "0")})")
 | 
			
		||||
        result.append(TickItem(resource, round(random.uniform(resource_rules[0][1], resource_rules[0][2]), 2), target))
 | 
			
		||||
    
 | 
			
		||||
    return result
 | 
			
		||||
 | 
			
		||||
## \brief A tick item.
 | 
			
		||||
class TickItem(object):
 | 
			
		||||
    def __init__(self, resource, amount, target):
 | 
			
		||||
        if resource not in valid_resources:
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,8 @@ import json
 | 
			
		||||
 | 
			
		||||
from . import items
 | 
			
		||||
 | 
			
		||||
## \brief Extended JSON encoder object to handle seagull-specific items.
 | 
			
		||||
#  \internal
 | 
			
		||||
class JSONizer(json.JSONEncoder):
 | 
			
		||||
    def default(self, obj):
 | 
			
		||||
        if isinstance(obj, items.TickItem):
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ import subprocess
 | 
			
		||||
 | 
			
		||||
import flask
 | 
			
		||||
 | 
			
		||||
from . import core, items, jsonizer
 | 
			
		||||
from . import core, gamedata, items, jsonizer
 | 
			
		||||
 | 
			
		||||
rant_env = os.environ.copy()
 | 
			
		||||
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"
 | 
			
		||||
    else:
 | 
			
		||||
        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()
 | 
			
		||||
 | 
			
		||||
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(11, 2, "ENCGULL"))
 | 
			
		||||
 | 
			
		||||
## \brief Returns the results of a game tick.
 | 
			
		||||
#  \api{GET} /tick
 | 
			
		||||
#  \return Game tick events and results.
 | 
			
		||||
@core.app.route("/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]
 | 
			
		||||
@@ -67,10 +71,28 @@ def tick():
 | 
			
		||||
 | 
			
		||||
    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"])
 | 
			
		||||
def tick_colony() -> flask.Response:
 | 
			
		||||
    req = flask.request.json
 | 
			
		||||
    if not req:
 | 
			
		||||
        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
 | 
			
		||||
pyqtwebengine>=5.15.7
 | 
			
		||||
pywebview[qt]>=5.4
 | 
			
		||||
pyqt6-webengine>=6.9.0
 | 
			
		||||
pywebview[qt6]>=5.4
 | 
			
		||||
@@ -1,4 +1,6 @@
 | 
			
		||||
Flask>=3.1.1
 | 
			
		||||
flask-cors>=6.0.1
 | 
			
		||||
gevent>=25.5.1
 | 
			
		||||
lxml>=6.0.0
 | 
			
		||||
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% }
 | 
			
		||||
 | 
			
		||||
/** MAIN GAME **/
 | 
			
		||||
 | 
			
		||||
div#root {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
@@ -50,6 +52,7 @@ div#main-day-stats {
 | 
			
		||||
    margin-top: auto;
 | 
			
		||||
    margin-bottom: auto;
 | 
			
		||||
    vertical-align: middle;
 | 
			
		||||
    font-size: large;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
div#main-button-bar {
 | 
			
		||||
@@ -101,37 +104,129 @@ div#charsheet {
 | 
			
		||||
    background-color: rgb(240, 240, 240);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
div#charsheet-leftside {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
div#charsheet-rightside {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
div.attr {
 | 
			
		||||
    font-size: larger;
 | 
			
		||||
}
 | 
			
		||||
/** MODAL **/
 | 
			
		||||
 | 
			
		||||
div#modal-background {
 | 
			
		||||
    z-index: -10;
 | 
			
		||||
    background-color: rgba(0, 0, 0, 168);
 | 
			
		||||
    visibility: hidden;
 | 
			
		||||
    font-family: sans-serif;
 | 
			
		||||
    background-color: rgba(0, 0, 0, 0.6);
 | 
			
		||||
    
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    position: fixed;
 | 
			
		||||
    top: 0;
 | 
			
		||||
    left: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
div#modal {
 | 
			
		||||
    width: 90%;
 | 
			
		||||
    height: 90%;
 | 
			
		||||
    border: 1.25em double rgba(192, 192, 192, 255);
 | 
			
		||||
    width: 75%;
 | 
			
		||||
    height: 75%;
 | 
			
		||||
    margin: auto;
 | 
			
		||||
    margin-top: 50px;
 | 
			
		||||
    border: 0.25em dotted rgba(192, 192, 192, 255);
 | 
			
		||||
    background-color: rgba(255, 255, 255, 255);
 | 
			
		||||
    padding: 0.3em
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
button.main-bar {
 | 
			
		||||
    width: 2.5em;
 | 
			
		||||
    height: 2.5em;
 | 
			
		||||
    margin: 2.5px;
 | 
			
		||||
    background-color: rgba(0,0,0,0);
 | 
			
		||||
    border: 1px solid black;
 | 
			
		||||
    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;
 | 
			
		||||
var tick_meter_running = false;
 | 
			
		||||
globalThis.desktop_mode = true;
 | 
			
		||||
 | 
			
		||||
async function prepare_gamestate() {
 | 
			
		||||
    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");
 | 
			
		||||
 | 
			
		||||
    if (gamestate_loaded == null) {
 | 
			
		||||
@@ -2,13 +2,16 @@ const ver_numeric = 0;
 | 
			
		||||
const ver_string = "pre alpha";
 | 
			
		||||
 | 
			
		||||
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 gamestate = {};
 | 
			
		||||
globalThis.gamestate = {};
 | 
			
		||||
globalThis.tick_meter_running = false;
 | 
			
		||||
var ticks_since_last_save = 0;
 | 
			
		||||
 | 
			
		||||
const gamestate_default = {
 | 
			
		||||
globalThis.gamestate_default = {
 | 
			
		||||
    "statever": "1",
 | 
			
		||||
    "tick": 1,
 | 
			
		||||
    "name": "Nameless",
 | 
			
		||||
@@ -25,11 +28,28 @@ const gamestate_default = {
 | 
			
		||||
    "enc_seagull": "pause",
 | 
			
		||||
    "agility": 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
 | 
			
		||||
function record_log(text) {
 | 
			
		||||
globalThis.record_log = function (text) {
 | 
			
		||||
    const div_logrow = document.createElement("div");
 | 
			
		||||
    if (bool_log_alt) { div_logrow.className = "log-line"; }
 | 
			
		||||
    else { div_logrow.className = "log-line-alt"; }
 | 
			
		||||
@@ -87,37 +107,91 @@ function record_log_with_choices() {
 | 
			
		||||
    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 = [];
 | 
			
		||||
 | 
			
		||||
function modal_no_prop(event) { event.stopPropagation(); }
 | 
			
		||||
 | 
			
		||||
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) {
 | 
			
		||||
        tick_meter_running = false;
 | 
			
		||||
        modal_dialog_open = true;
 | 
			
		||||
        modal_background.style.zIndex = "10 !important";
 | 
			
		||||
        modal_background.style.visibility = "visible !important";
 | 
			
		||||
    }
 | 
			
		||||
        modal_dialog_name = dialog;
 | 
			
		||||
 | 
			
		||||
    dialog_data = await fetch(`/dialog/${dialog}`)
 | 
			
		||||
        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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (dialog_queue.length == 0) {
 | 
			
		||||
        modal_background.style.zIndex = "-10";
 | 
			
		||||
        modal_background.style.visibility = "hidden";
 | 
			
		||||
        document.body.removeChild(modal_background);
 | 
			
		||||
 | 
			
		||||
        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() {
 | 
			
		||||
@@ -126,6 +200,8 @@ function update_ui() {
 | 
			
		||||
    page_elements["lbl_colony"].innerHTML = gamestate["colony"];
 | 
			
		||||
    page_elements["lbl_shinies"].innerHTML = gamestate["shinies"].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_xp"].innerHTML = gamestate["xp"];
 | 
			
		||||
    page_elements["lbl_xp_next"].innerHTML = gamestate["xp_next"];
 | 
			
		||||
@@ -160,10 +236,11 @@ function dev_toolbox(open) {
 | 
			
		||||
function reward_xp(amount) {
 | 
			
		||||
    gamestate["xp"] += amount;
 | 
			
		||||
    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["level"] += 1;
 | 
			
		||||
        gamestate["xp_next"] = (old_xp_next * 1.5) + (gamestate["level"] * 5);
 | 
			
		||||
        record_log(`You have advanced to level ${gamestate["level"]}.`);
 | 
			
		||||
 | 
			
		||||
        if (gamestate["level"] == 2) {
 | 
			
		||||
            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 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(); })
 | 
			
		||||
    .catch(e => { throw e; });
 | 
			
		||||
 | 
			
		||||
    if (stealdata["success"] && amount > 0) {
 | 
			
		||||
        gamestate[resource] += amount;
 | 
			
		||||
        tickdiffs[resource] += amount;
 | 
			
		||||
        reward_xp(2);
 | 
			
		||||
        record_log(`Stole ${resource} from a ${target}: ${items.join(", ")}`);
 | 
			
		||||
    }
 | 
			
		||||
    else { record_log(`Didn't steal ${resource} from a ${target}`); }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function recruit(amount) {
 | 
			
		||||
globalThis.recruit = async function (amount) {
 | 
			
		||||
    if (gamestate["shinies"] < amount) {
 | 
			
		||||
        record_log("You do not have enough shinies to recruit this seagull.");
 | 
			
		||||
        return;
 | 
			
		||||
@@ -208,13 +286,53 @@ async function recruit(amount) {
 | 
			
		||||
    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() {
 | 
			
		||||
    gamestate["tick"] += 1;
 | 
			
		||||
    ticks_since_last_save += 1;
 | 
			
		||||
    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")
 | 
			
		||||
    .then(res => {
 | 
			
		||||
        json = res.json()
 | 
			
		||||
        var json = res.json()
 | 
			
		||||
        console.log(json)
 | 
			
		||||
        return json
 | 
			
		||||
    })
 | 
			
		||||
@@ -352,6 +470,15 @@ async function game_tick() {
 | 
			
		||||
        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();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -374,6 +501,8 @@ function update_action(enc, value) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
target.addEventListener(start_event, function (ev) {
 | 
			
		||||
    tickdiffs = structuredClone(tickdiffs_reset);
 | 
			
		||||
 | 
			
		||||
    page_elements["div_log"] = document.querySelector("#main-log");
 | 
			
		||||
    page_elements["div_sidebar"] = document.querySelector("#main-sidebar");
 | 
			
		||||
    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_shinies"] = document.querySelector("#lbl-seagull-shinies");
 | 
			
		||||
    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["lbl_tick"] = document.querySelector("#main-day-counter");
 | 
			
		||||
    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_seagull"].addEventListener("change", (ev) => {update_action("seagull", ev.target.value)});
 | 
			
		||||
    page_elements["btn_charsheet"].addEventListener("click", (ev) => {open_modal_dialog("charsheet")});
 | 
			
		||||
    page_elements["btn_about"].addEventListener("click", (ev) => {open_modal_dialog("about")});
 | 
			
		||||
 | 
			
		||||
    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 }}">
 | 
			
		||||
{%- endfor -%}
 | 
			
		||||
{%- 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 -%}
 | 
			
		||||
</head>
 | 
			
		||||
<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-misc">
 | 
			
		||||
            Colony: <span id="lbl-seagull-colony">1337</span><br />
 | 
			
		||||
            Shinies: <span id="lbl-seagull-shinies">420</span><br />
 | 
			
		||||
            Food: <span id="lbl-seagull-food">69</span>
 | 
			
		||||
            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> <i>(+<span id="lbl-seagull-food-income">0</span>/day)</i>
 | 
			
		||||
        </p>
 | 
			
		||||
    </div>
 | 
			
		||||
    <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(
 | 
			
		||||
    ['app/desktop.py'],
 | 
			
		||||
    ['@CMAKE_SOURCE_DIR@/app/desktop.py'],
 | 
			
		||||
    pathex=[],
 | 
			
		||||
    binaries=[],
 | 
			
		||||
    datas=[
 | 
			
		||||
        ('app/rant', 'basepak/rant'),
 | 
			
		||||
        ('app/rules/schemas', 'basepak/rules/schemas'),
 | 
			
		||||
        ('@CMAKE_SOURCE_DIR@/app/basepak', 'basepak'),
 | 
			
		||||
        ('@CMAKE_CURRENT_BINARY_DIR@/basepak', 'basepak'),
 | 
			
		||||
        ('opt', './opt')
 | 
			
		||||
    ],
 | 
			
		||||
    hiddenimports=[],
 | 
			
		||||
@@ -20,8 +20,14 @@ a = Analysis(
 | 
			
		||||
)
 | 
			
		||||
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(
 | 
			
		||||
    pyz,
 | 
			
		||||
    splash,
 | 
			
		||||
    splash.binaries,
 | 
			
		||||
    a.scripts,
 | 
			
		||||
    a.binaries,
 | 
			
		||||
    a.datas,
 | 
			
		||||
		Reference in New Issue
	
	Block a user