giant freakin documentation and reorganization pass, it also uses cmake because all the building was getting too complicated for shell scripts
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -7,5 +7,8 @@ seagull.pak
|
||||
build/**
|
||||
build_cache/**
|
||||
|
||||
# autogenerated
|
||||
doc/**
|
||||
|
||||
**/__pycache__/**
|
||||
**.pyc
|
@@ -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
|
||||
)
|
||||
|
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