Dump
Template
Dump of Random Notes
^$SECTION
Implement Firebase App Check with reCAPTCHA v3 that creates an invisible challenge to the browser / user App Check automatically attaches tokens to all Firebase requests This gives some protection to bots
^$SECTION
Windwaker save file location
- GCI folder vs Game card file
Dolphin Emulator
- dolphin-x64 v2503
- Before first run add
portable.txt
file to the dolphin folder- Be extra careful that the file has not been named
portable.txt.txt
- Be extra careful that the file has not been named
- Add a Roms folder, and add that to paths for Dolphin
- Edit User/Config/Dolphin.ini to have Roms as a relative path instead of absolute
- Edit User/Config/Dolphin.ini to have WFS as a relative path instead of absolute
- Make sure all paths in Settings/Paths start with User
- Ensure nothing has been written to app data roaming
- Replace the contents of User/GC with correct save files
VSCode
- You can search all repositories on your account for any text in a filename or within a file
^$SECTION
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class SaveManager {
static savingNow = false;
static savingSoon = false;
static delay = 3000;
/**
* Implement custom asynchronous save function here
* @returns {Promise<null>}
*/
static async _save() {
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
await delay(this.delay);
console.log('Save complete');
}
/**
* Initiate an immediate save if none is in progress
* @returns {Promise<null>}
*/
static async saveNow() {
if (this.savingNow) return;
this.savingNow = true;
try {
await this._save();
}
finally {
this.savingNow = false;
}
}
/**
* Schedule a save after a delay unless one is queued or in progress
*/
static saveSoon() {
if (this.savingSoon || this.savingNow) return;
this.savingSoon = true;
setTimeout(() => {
this.savingSoon = false;
this.saveNow();
}, this.delay);
}
}
^$SECTION
ChatGPT
- 4o mini Claude
- Claude 3.7 Sonnet
- Claude 3.5 Haiku
Make a link to open notes repository in a container / on web
Hide the central element when showing table, intstead of hiding active note
Focus at end of active note when tab loads
Remove the focus border when editing tab name
Stop ENTER key being usable when editing tab name
Learn about Python pipe, where the result of each function is passed to the next
Base64 encoding is used as a bridge to send binary as text to communicate between devices that prefer one or the other
^$SECTION
() Realtime Database: Configure a security rules file for Realtime Database and (optionally) provision default instance ( ) Data Connect: Set up a Firebase Data Connect service () Firestore: Configure security rules and indexes files for Firestore
( ) Genkit: Setup a new Genkit project with Firebase () Functions: Configure a Cloud Functions directory and its files ( ) App Hosting: Configure an apphosting.yaml file for App Hosting () Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys ( ) Storage: Configure a security rules file for Cloud Storage ( ) Emulators: Set up local emulators for Firebase products ( ) Remote Config: Configure a template file for Remote Config ( ) Extensions: Set up an empty Extensions manifest
[F] () Realtime Database: Configure a security rules file for Realtime Database and (optionally) provision default instance
[P] ( ) Data Connect: Set up a Firebase Data Connect service
[F] () Firestore: Configure security rules and indexes files for Firestore
[F] >( ) Genkit: Setup a new Genkit project with Firebase
[F] () Functions: Configure a Cloud Functions directory and its files
[P] ( ) App Hosting: Configure an apphosting.yaml file for App Hosting
[F] () Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys
[P] ( ) Storage: Configure a security rules file for Cloud Storage
[F] ( ) Emulators: Set up local emulators for Firebase products
[P] ( ) Remote Config: Configure a template file for Remote Config
[P] ( ) Extensions: Set up an empty Extensions manifest
From what I have experienced so far, you’ll need the functions only for Firebase admin utilities. And since most of my projects use Next.js, I can use the easy-to-use serverless API feature of the framework for any Firebase admin needs. Also, Next.js projects can be deployed on Vercel, a production grade hosting platform created by Next.js creators themeselves, with minimal configurations. All in all, I have deployed 2 production level projects using the Firebase-Next-Vercel combo and I have had to pay $0 so far.
^$SECTION
Screnshot (245) - Set up sign in for the site
- Ignore SHA warning, it is for android apps
- Just enable it and give the project a name and support email
Screnshot (246) - Add localhost to authorised domains
- Go to authentication / settings / authorised domains
There are lots of files when you setup a firebase project, but most are for backend use, and you can get away with the CDN usage to interface with the firebase backend - easiest if you use some sort of authorisation
Screenshot (247) - Authorisation for Firebase
is done via IndexedDB
instead of localStorage
- Stored as firebaseLocalStorageDb/firebaseLocalStorage
- This is automatically read / written via
getAuth
fromfirebase-auth
Firestore
- Both firstore and realtime database are json trees
- Firestore has a “add collection” button
Learnings
- indexedDB allows persistent storage of objects, where localStorage only allows strings
- indexedDB allows single key / value pair manipulation
- it is asynchronous
- SDK is a Software Developement Kit
Ideas
- Google auth to get openAI key to use on site? as a token or somethin
^$SECTION
“dev”, “webdev”, “programming”, “coding”, “python”, “javascript”, “html”, “css”, “wasm”, “pyodide”, “ace”, “ace editor”, “jsonblob”, “api”, “static site”, “github pages”, “cloud storage”, “toolbar”, “google material icons”, “requests”, “http methods”, “post request”, “get request”, “fetch”
“dev”, “webdev”, “programming”, “coding”, “javascript”, “html”, “css”, “cdn”, “ecma6”, “firebase”, “firestore”, “firestore rules”, “cloud firestore”, “firebase cli”, “authentication”, “google oauth 2.0”, “api”, “api keys”, “encryption”, “pbkdf2”, “https”, “tokens”, “refresh tokens”, “id tokens”
^$SECTION
Pure JavaScript Reference Guide
- Use void keyword to discard the return value of a function call or expression
- Components are javascript files that are self contained and control their own styling / css / functionality
- Synchronous code with Promise chaining via .then might be advisable for games in the sense that you can pass something off to be executed later, while also giving a callback to run when that does get executed
- Promise has resolve in its callback thats passed, and when the resolve is called resolve(), that result is passed to .then, the function in then should be expecting one argument “result”
- Connected callback fires again if it is an object that moves containers, or if it is a child of an object that moves containers
^$SECTION
Use display: inline-flex
or display: inline
for buttons so they only take up their own text length rather than expanding to fill space
^$SECTION
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
/* style-enhanced-v1.0.0 */
/* < ====================================================== <
< Root Variables
< ====================================================== < */
:root {
/* > ===================================================
> Section I: Colour Palettes
> =================================================== */
--colour-neutral-50: #fafafa;
--colour-neutral-100: #f5f5f5;
--colour-neutral-200: #e5e5e5;
--colour-neutral-300: #d4d4d4;
--colour-neutral-400: #a3a3a3;
--colour-neutral-500: #737373;
--colour-neutral-600: #525252;
--colour-neutral-700: #404040;
--colour-neutral-800: #262626;
--colour-neutral-900: #171717;
--colour-warning-50: #fffbeb;
--colour-warning-100: #fef3c7;
--colour-warning-200: #fde68a;
--colour-warning-300: #fcd34d;
--colour-warning-400: #fbbf24;
--colour-warning-500: #f59e0b;
--colour-warning-600: #d97706;
--colour-warning-700: #b45309;
--colour-warning-800: #92400e;
--colour-warning-900: #78350f;
--colour-error-50: #fef2f2;
--colour-error-100: #fee2e2;
--colour-error-200: #fecaca;
--colour-error-300: #fca5a5;
--colour-error-400: #f87171;
--colour-error-500: #ef4444;
--colour-error-600: #dc2626;
--colour-error-700: #b91c1c;
--colour-error-800: #991b1b;
--colour-error-900: #7f1d1d;
--colour-success-50: #f0fdf4;
--colour-success-100: #dcfce7;
--colour-success-200: #bbf7d0;
--colour-success-300: #86efac;
--colour-success-400: #4ade80;
--colour-success-500: #22c55e;
--colour-success-600: #16a34a;
--colour-success-700: #15803d;
--colour-success-800: #166534;
--colour-success-900: #14532d;
--colour-info-50: #eff6ff;
--colour-info-100: #dbeafe;
--colour-info-200: #bfdbfe;
--colour-info-300: #93c5fd;
--colour-info-400: #60a5fa;
--colour-info-500: #3b82f6;
--colour-info-600: #2563eb;
--colour-info-700: #1d4ed8;
--colour-info-800: #1e40af;
--colour-info-900: #1e3a8a;
--colour-fuchsia-50: #fdf4ff;
--colour-fuchsia-100: #fae8ff;
--colour-fuchsia-200: #f5d0fe;
--colour-fuchsia-300: #f0abfc;
--colour-fuchsia-400: #e879f9;
--colour-fuchsia-500: #d946ef;
--colour-fuchsia-600: #c026d3;
--colour-fuchsia-700: #a21caf;
--colour-fuchsia-800: #86198f;
--colour-fuchsia-900: #701a75;
--colour-sky-50: #f0f9ff;
--colour-sky-100: #e0f2fe;
--colour-sky-200: #bae6fd;
--colour-sky-300: #7dd3fc;
--colour-sky-400: #38bdf8;
--colour-sky-500: #0ea5e9;
--colour-sky-600: #0284c7;
--colour-sky-700: #0369a1;
--colour-sky-800: #075985;
--colour-sky-900: #0c4a6e;
--colour-rose-500: #f43f5e;
--colour-pink-500: #ec4899;
--colour-purple-500: #a855f7;
--colour-violet-500: #8b5cf6;
--colour-indigo-500: #6366f1;
--colour-blue-500: #3b82f6;
--colour-cyan-500: #06b6d4;
--colour-teal-500: #14b8a6;
--colour-emerald-500: #10b981;
--colour-green-500: #22c55e;
--colour-lime-500: #84cc16;
--colour-yellow-500: #eab308;
--colour-amber-500: #f59e0b;
--colour-orange-500: #f97316;
--colour-red-500: #ef4444;
--colour-stone-500: #78716c;
--colour-zinc-500: #71717a;
--colour-slate-500: #64748b;
--colour-custom-01: hsl(212, 10%, 37%);
/* > ===================================================
> Section II: Font Families
> =================================================== */
--font-family-sans: Inter, 'Segoe UI', sans-serif;
--font-family-roboto: Roboto, Arial, sans-serif;
--font-family-montserrat: Montserrat, 'Trebuchet MS', sans-serif;
--font-family-serif: Georgia, 'Times New Roman', serif;
--font-family-mono: 'Courier New', Courier, monospace;
--font-family-cursive: 'Comic Sans MS', cursive, sans-serif;
/* << =================================================
<< Section III: Project Theme Variables
<< ================================================= */
--theme-text: var(--colour-neutral-300);
--theme-text-soft: var(--colour-sky-400);
--theme-text-rich: var(--colour-sky-200);
--theme-accent: var(--colour-fuchsia-500);
--theme-accent-soft: var(--colour-fuchsia-400);
--theme-accent-rich: var(--colour-fuchsia-600);
--theme-foreground: var(--colour-success-500);
--theme-foreground-soft: var(--colour-success-400);
--theme-foreground-rich: var(--colour-success-600);
--theme-background: var(--colour-neutral-800);
--theme-background-soft: var(--colour-neutral-700);
--theme-background-rich: var(--colour-neutral-900);
--theme-shadow: inset 0 0 0 2px var(--colour-fuchsia-500);
--theme-scrollbar: var(--colour-custom-01) transparent;
/* << =================================================
<< Section IV: Other Project Variables
<< ================================================= */
--font-size: 16px;
--font-family: var(--font-family-mono);
--font-weight: 400;
}
/* < ====================================================== <
< HTML & Body Styling
< ====================================================== < */
html,
body {
height: 100%;
width: 100%;
margin: 0px;
padding: 0px;
box-sizing: border-box;
overflow: auto;
background-color: var(--theme-background);
color: var(--theme-text);
font-family: var(--font-family);
font-size: var(--font-size);
font-weight: var(--font-weight);
scrollbar-color: var(--theme-scrollbar);
}
/* < ====================================================== <
< Wildcard Styling
< ====================================================== < */
* {
scrollbar-color: var(--theme-scrollbar);
/* box-sizing: border-box; */
/* box-shadow: var(--theme-shadow); */
}
/* < ====================================================== <
< Utility Classes
< ====================================================== < */
.hidden {
display: none !important;
}
/* < ====================================================== <
< Page Element Styling
< ====================================================== < */
#page {
height: 100%;
width: 100%;
margin: 0px;
padding: 0px;
box-sizing: border-box;
display: flex;
flex-direction: column;
position: relative;
background-color: transparent;
}
/* << ==================================================== <<
<< Project Styling
<< ==================================================== << */
#header {
width: 100%;
height: 64px;
box-sizing: border-box;
padding: 12px 16px;
display: flex;
align-items: center;
overflow-x: auto;
overflow-y: hidden;
color: var(--theme-text);
background-color: var(--theme-background-soft);
border-bottom: 1px solid var(--colour-neutral-600);
}
#body {
width: 100%;
height: auto;
box-sizing: border-box;
flex: 1;
overflow: auto;
}
.window {
width: 100%;
height: 100%;
box-sizing: border-box;
padding: 12px 16px;
background-color: var(--theme-background);
}
#content {
color: var(--theme-text);
}
#error {
color: var(--colour-error-500);
}
/* ! ====================================================== !
! Experimental
! ====================================================== ! */
.profile-pic {
width: 48px;
height: 48px;
border-radius: 50%;
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAFklEQVQYV2NkQAN/Gf4z/38GDAwMDAwAwB8kC8n4CyJkAAAAAElFTkSuQmCC');
background-size: cover;
background-position: center;
/* fill: red; */
background-color: blue;
/* color: green; */
}
^$SECTION
You can test HTML
/ CSS
/ JavaScript
on GitHub Codespaces
using npm install -g live-server
then live-server
A codespace
stays active for about 30 minutes after you close the tab, and will continue running any server that you initiate.
It creates localhost type sites at GitHub
URLs that you can access via another device logged in with your credentials
GitHub codespaces can be given setup .json
files that dictate their environment
As the codespace
virtual machine runs in the cloud you can technically code on any device you wish, regardless of its power
^$SECTION
Hiding HTML Elements Dynamically using Javascript and CSS
You can hide elements in HTML dynamically using Javascript to set CSS property display
of the element to "none"
as in item.style.display = "none"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function filterItems(tag) {
// Select all elements with the class "item"
const items = document.querySelectorAll(".item");
// Iterate over all items
items.forEach((item) => {
// Extract tags from item's dataset, stored as a space delimited list
const tags = item.dataset.tags.split(" ");
// Check if the specified tag is present in the item's tags
if (tags.includes(tag)) {
item.style.display = "inline-block"; // If yes, display the item
} else {
item.style.display = "none"; // If not, hide the item
}
});
}
In the example above, items are only displayed if one of their tags matches the filtered tag in filterItems(tag)
. In this way HTML elements can be hidden from a search bar or button
^$SECTION
Converting Markdown Text to HTML Using Showdown and Javascript
Information About Importing Javascript via CDN / NPM
There are multiple ways to import a script in HTML / Javascript, but a simple way is to import it via a content delivery network (CDN) such as jsDelivr. The search function for jsDelivr can be found here. In simple terms jsDelivr pulls packages from the NPM (Node Package Manager) registry, it automatically handles versioning and caching. When you reference a specific version of a package, jsDelivr can serve that version with appropriate caching headers, ensuring that users receive the correct and cached version of the asset.
The example below imports showdown.min.js
before script.js
so that script.js
can access Showdown.
1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Notes</title>
<script src="https://cdn.jsdelivr.net/npm/showdown@2.1.0/dist/showdown.min.js"></script>
<script src="script.js"></script>
<link rel="stylesheet" href="styles.css">
</head>
Using Showdown in Javascript
The core of Showdown is using converter = new Showdown.converter()
to create a converter object and using html = converter.makeHtml(text);
to convert markdown strings using the converter object, as with the example below.
1
2
3
4
5
6
7
8
// Store test markdown string as variable
var text = "Markdown **rocks**";
// Store showdown converter object as variable
var converter = new Showdown.converter();
// Convert markdown to html and store html content as variable
var html = converter.makeHtml(text);
^$SECTION
Heading
CTRL + P
to search by filename or foldername inVSCode
- You can add a
.exe
file to aGitHub
release- This keeps is separate from source code
- Use
sys.exit()
in Python scripts instead ofquit()
when attempting to freeze to.exe
file - Benefits of
.gitignore
fordist
andbuild
folders - Find my file on how to make a venv
PyIntaller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Build guide:
- Disable windows defender real-time protection
- Run cmd in directory of shithead.py
- pyinstaller shithead.py --onefile --windowed --add-data "monogram.ttf;." --add-data "card_sheet_53x70_readable.png;." --add-data "card_sheet_53x70_flipped.png;." --add-data "card_sheet_53x70_back_alt.png;." --add-data "shithead.png;." --icon "shithead.ico"
- Extract shithead.exe from dist folder
- Not sure if --icon "shithead.ico" or --icon=shithead.ico
444 INFO: PyInstaller: 6.0.0
2528 INFO: Python: 3.10.5
2538 INFO: Platform: Windows-10-10.0.19045-SP0
2860 INFO: wrote G:\NEW\Coding\Applications\pyinstaller\shithead\builder\shithead.spec
2863 INFO: Extending PYTHONPATH with paths
['G:\\NEW\\Coding\\Applications\\pyinstaller\\shithead\\builder']
pygame-ce 2.3.2 (SDL 2.26.5, Python 3.10.5)
- Find my batch learning document
- Make an INSTALLER.bat document
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import os
# __DIR__ contains the actual directory that this file is in.
__DIR__ = getattr(sys, '_MEIPASS', os.path.abspath(os.path.dirname(__file__)))
# ...
# Now you can access your image this way (and it will work on all platforms 😃):
tkinter.PhotoImage(file = os.path.join(__DIR__, "Images", "btnOK.png"))
In order to preview a markdown file in **VSCode** use `CTRL` + `SHIFT` + `V`
The **link syntax** `[Link Text](website URL)` in markdown is similar to the **image syntax** except that the image syntax requires an exclamation point `!` to denote that the link is an image: `` where `title` is the name of the image and `alt text` is the text that will display if the image cannot be loaded
In order to view a local image in markdown use `` where `local_image.png` is found in the same directory as the markdown file
The image can be seen below:

To add a new line in markdown add a double space at the end of a line: `line ends` ` ` ` `
^$SECTION
Curiousities
- GitHub pages
- GitHub packages
- Can allow you to work with a consistent set of interconnected packages
- Consistent workspace across multiple projects
- You can
pip install
GitHub packages directly via URL - git+https://github.com/jkbr/httpie.git#egg=httpie
- Remember the egg thing as it names the project
- Or
pip install --upgrade https://github.com/jkbr/httpie/tarball/master
- Or
git clone https://github.com/jkbr/httpie.git
andpython setup.py install
- Can allow you to work with a consistent set of interconnected packages
- GitHub codespaces
- https://github.com/agapasieka/cfg-project-group5
- GitHub suggested workflows
- Learn GitHub branches
- Installing Python package from a GitHub repo
- force-reinstall
- Install upgrade
- Install from branch or tag
- So you could force reinstall your package from the experimental branch to test new features
- Remember editable and egg
- SSH
- GitHub remote repositories
- “Remote environment thing”
- src layout for a Python package has tests outside your package and so will not run via local files, only via installed package, but then users cannot run tests, so include if you want
- src layout means local directory files that are on python path do not include your package, therefore you will have to install the package
- Dependabot
- https://about.readthedocs.com/?ref=readthedocs.org
- exit is a helper for the interactive shell - sys.exit is intended for use in programs.
Poetry
for package management inPython
here- What on fuck is a wheel
Package Entry Points
1
2
3
4
5
6
7
8
9
10
5. entry_points (Optional)
If your package includes command-line tools or scripts, you can define entry points for these. For example:
python
Copy code
entry_points={
'console_scripts': [
'my-command = mypackage.module:function',
],
},
In command line python -c "from template_package.moduleA import test; test()"
C:\...\template-package> pip install -e .
Obtaining file:///C:/.../template-package
Preparing metadata (setup.py) ... done
Installing collected packages: template-package
Running setup.py develop for template-package
Successfully installed template-package-0.1.0
C:\...\template-package> pip show template-package
Name: template-package
Version: 0.1.0
Summary: Template package for Python
Home-page: https://github.com/username/template-package
Author: username
Author-email: username@email.com
License: MIT
Location: C:\...\template-package\src
Editable project location: C:\...\template-package\src
Requires:
Required-by:
C:\...\template-package> pip uninstall template-package
Found existing installation: template-package 0.1.0
Uninstalling template-package-0.1.0:
Would remove:
C:\...\python\site-packages\template-package.egg-link
Proceed (Y/n)? y
Successfully uninstalled template-package-0.1.0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
AUTHOR = 'username'
EMAIL = 'username@email.com'
PACKAGE_NAME = 'template-package'
VERSION = '0.1.0'
PACKAGE_LOCATION = 'src'
URL = f'https://github.com/{AUTHOR}/{PACKAGE_NAME}'
LICENSE = 'MIT'
DESCRIPTION = 'Template package for Python'
PYTHON_VERSION = '>=3.7'
REQUIREMENTS = []
with open('README.md', "r") as f:
LONG_DESCRIPTION = f.read()
LONG_DESCRIPTION_TYPE = "text/markdown"
Without using tools such as Poetry
The pyproject.toml
file tells build frontend tools like pip and build which backend to use for your project. Below are some examples for common build backends, but check your backend’s own documentation for more details.
pyproject.toml as the Modern Standard:
Introduced by PEP 518, pyproject.toml
1
2
3
[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"
Therefore you don’t really need both setup.py and pyproject.toml
pyproject.toml is the new standardized format to describe project metadata declaratively, introduced with PEP 621. It’s easier to work with and allows for shared configuration between different tools.
setup.py is the old de facto standard for packaging. Its main downside is that it requires you to run arbitrary, possibly untrusted Python code to even figure out what the project you’re dealing with is and how to build it.
setup.cfg is a convenience feature in setuptools, the library usually used for writing setup.py files, which allows for a more declarative format, but still requires a setup.py even if it just calls one function.
From what I understand, they are all the same, just written differently
Incorrect.
A package can work with setup.py only
Also incorrect.
so why use all of them?
You don’t, you pick one.
setup.py is literally the Python script that will install the package. It is customary to use various setuptools methods and a standardized set of data inside, but that’s not required. You can manually install a package by downloading it and executing setup.py, which is what setuptools (and therefore pip) does with several other safeguards and sophistication.
Therefore, setup.py contains enough information to describe a package but contains almost no information on a project. Many sidecar formats were used to remedy this, including setup.cfg. These contain the project information missing from setup.py that allows for other tools to work. However, this usually meant that much information was shared between the two files, which made things cumbersome.
Thus was born pyproject.toml, where package and project information are combined into a single source. This file improves usability and clarity and solves the duplicated information issue. The file data is also generalized so that any tool can use it as a data store. You can produce a setup.py using a pyproject.toml as the latter has at least as much information as the former requires, and many tools do this for backwards-compatible packages. However, it’s entirely possible (and is the direction Python packaging is moving) to use pyproject.toml directly. That is, pyproject.toml describes what you want to happen, while setup.py describes how. By letting the installation tools make intelligent choices on how to install the package pyproject.toml avoids many issues that setup.py currently causes.
Overall, just use poetry or some other project-management tool and let it deal with your project dependencies.
Uninstalling will delete egg
^$SECTION
HTML
- ARIA stands for Accessible Rich Internet Applications and is often found in HTML tags such as
<button type="button" data-key="q" aria-label="add q" aria-disabled="false">q</button>
- It is used primarily for accessibility purposes, such as for screen readers
- Classes in HTML can be strung together, separated by spaces, and styles will be inherited from all given classes
- Order in the stylesheet will matter if two classes affect the same style
- JavaScript can alter classes using
classList.add()
,classList.remove()
andclassList.toggle()
- HTML / CSS have a specificity scoring system to calculate the styling system that takes precedence
HTML allows custom data attributes in the format
data-*
, it can be anything you want reallydata-state
,data-name
,data-something
etc.- CSS outlines are annoying because they change element size and layout, try
box-shadow
instead withinset
keyword such asbox-shadow: inset 0 0 0 2px #3A3A3C
- The third zero stops it being
shadowy
and makes it a solid fill colour - Creates an inset outline with no change to size or layout
- The third zero stops it being
You can make a div act as button by giving it role=”button” and tabindex=”0” which makes it focusable for user
Use
<button type="button">
to stop button from submitting a form, which is the default- In JavaScript
let
isblock-scoped
and is only accessible in the block it is declared, such as afor-loop
orconditional
^$SECTION
Python Slice() Function for Named Slicing
In Python, the slice()
function is a powerful tool for creating slices or sub-sequences of sequences like lists, strings, or tuples. It provides a convenient way to define named slices, making your code more readable and expressive.
Basic Usage
The slice()
function takes three parameters: start
, stop
, and step
. These parameters represent the start index, stop index, and step value of the slice, respectively.
1
2
3
4
5
6
7
# Using slice() for named slicing
my_slice = slice(start, stop, step)
result = sequence[my_slice]
# Named slicing using slice()
last_five = slice(-5, None) # Creating a named slice for the last five elements
result = my_list[last_five] # Equivalent to my_list[-5:]
When creating a slice using slice(start, stop, step)
, if you use None
for the stop
parameter, it indicates that the slice should include all elements from the specified start
index up to the end of the sequence.
^$SECTION
Slice Assignment in Python
Slice assignment allows modifying parts of a list in place using the syntax my_list[start:stop] = new_values
.
The original list maintains its space in memory, hence the operations is considered in place, even if it occurs within a function.
1
2
3
4
5
lst = [1, 2, 3, 4, 5, 6]
def foo(lst):
lst[1:4] = [8, 9, 10] # Replaces elements at index 1 to 3 with [8, 9, 10]
lst[:] = [4, 5, 6] # Replaces the entire list with the list [4, 5, 6]
Slice assignment is a concise and efficient way to replace a list or update specific portions of a list without the use of the global keyword
^$SECTION
Process if a module is not recognised in CMD (Here we use Pyinstaller as the example)
Check Installation Location: Ensure that the location where PyInstaller is installed is included in your system’s PATH environment variable.
Add PyInstaller to PATH: You can manually add the directory containing the PyInstaller executable to your PATH. For example, if PyInstaller is installed in C:\Users\%username%\AppData\Local\Packages\pythonsoftwarefoundation.python.3.10_qbz5n2kfra8p0\LocalCache\Local-packages\Python310\Scripts, you should add this path to the system’s PATH.
To add a directory to the PATH in Windows, you can follow these steps:
Right-click on “This PC” or “Computer” and select “Properties.” Click on “Advanced system settings.” Click on the “Environment Variables” button. In the “System variables” section, find the “Path” variable and click “Edit.” Click “New” and add the path to the directory containing PyInstaller. Restart Command Prompt or Shell: After modifying the PATH, close and reopen your Command Prompt or shell to apply the changes.
Verify PyInstaller Installation: Once you’ve added the directory to the PATH, open a new Command Prompt and try running pyinstaller –version again. It should now recognize the command.
If the issue persists, make sure there are no typos in the directory path you added to the PATH variable. If you encounter any problems, feel free to provide more details, and I’ll do my best to assist you.
^$SECTION
Make GIF
tutorials with ScreenToGIF
^$SECTION
.gitignore
files allow for exceptions to rules using !
, for example, below .vscode/settings.json
would be ignored by the .vscode/*
rule but is excepted due to the !
prefix.
1
2
3
4
5
.idea
.vscode/*
!.vscode/settings.json
!.vscode/extensions.json
!.vscode/tasks.json
^$SECTION
VSCode Local Snippet
- Create a
.vscode
folder - Create a
{filetype}.code-snippets
file in the.vscode
folder eg.javascript.code-snippets
^$SECTION
collection_of_game_notes_and_ideas.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
# Search Tags:
# assorted notes, main notes, sorted, sorting, note collection, slay the spire, replication, reverse engineering, hurdles, problems, game notes, collection of game notes and ideas, game collection, note collection
""" Waffle Section
Cards themeselves are playable default, and may be playable on condition
The player can stop the play of a card based on the cost of the card
- The mana cost
- OR the Gold cost
The player can stop the play of the card based on its own set of restrictions
There has to be a way to get the cost of a card from the card itself and then run that through any ALTERATIONS
Most cards do something, or a list of somethings
Other cards do things based on conditions
Conditions for Playability
Clash = Playable if only attacks
Blood for Blood = Costs less for damage taken
Carnage = Ethereal on turn end
Special upgrades where cards get random attribute changes, eg. hits all targets, add recoil to the card, some could be good, some bad, and some of the bad could fit into synergies, eg. A recoil card that is double stacked injures you twice - enacting a self damage synergy twice (could have self damage strategies based on number of instances you take damage, and some on quantity, or some on quantity in a single turn, eg. lose 40HP in a turn and gain 20 strength.
Cursed - Halve Enemy HP, Double their Attack
Glass Cards one time use double effects, can be created at bonfires
Glass blowing relic to either make or improve Glass cards
Bioshock theme maybe
Random wheel spin card for 3 effects, damage, HP, money or something
Climb mountain with cart
Vending machine as shop and a hammer relic that opens one and gives all contents
Front enemy and back enemy, enemy attacks and your cards can have the sweep bonus to hit back ranks, or able to attack back minion directly
You can move your cart to the front, it takes damage in coins or maybe needs repairing in coins later, cards or relics to move it to the front
Relic to steal vending machine, they are taken into battle and can be attacked by yourself or enemies to give money and all the contents within
Relic that the first time your cart takes damage in a fight you GET that amount of damage in gold rather than lose it
Relic or card bonuses that synergises with a vending machine build, ie bonus damage or double effects when you have a vending machine
One man army build where you have no cart, presumably this would have to be an option at base camp
Base camp instead of the whale where your relics and first card / run choices are made, perhaps a drafting system for a few cards
Alchemy card that converts your HP or stamina or effort or something into gold, or fixes the cart, can be upgraded any number of times
Caves to heal or upgrade maybe, it should be a choice but not necessarily same location
Vending machine synergy cards would presumably repair Vending machine or you could replace with the next Vending machine you find
Rare grapple card to switch enemy order
Fight where killing minion at back is really dangerous, except it's a cute little mouse or something to start - and when hit it becomes the big boy and super angry
Another fight where once the tiny minion dies the main fighter gets angry, meaning in both these fights you need to be tactical
Ability/Potion to send in a dummy, you rest here and send in a dummy, if it dies nothing is lost, if it wins you win kinda thing, essentially an extra life, if its a non boss enemy you enter the fight again with their low HP - eg a boss will heal and a normal enemy will not
Deal Damage equal to the total mana cost of cards in hand, upgrade to cards in deck? Make it possible to make a card undrawable rather than remove it, have benefits to having a small deck that make removing more favourable, but perhaps have a card that increases in mana cost each battle, or events to increase mana cost, perhaps with some other benefits but would be fun to try this as a strategy, maybe quite hard if it's only one card, maybe a class of attack cards that get bonus for mana in hand or deck making a whole synergy
Event to make a card cost one mana, perhaps a similar upgrade system to monster train, certain cards can be upgraded a certain number of times or get gems in them or something
Link to the idea of hedonistism/euphoric death, climbing the mountain to get revenge on someone and therefore find peace in death
"""
# > Slay the Spire Cards
# Deal 6 Damage
# Deal 8 Damage, Apply 2 Vulnerable
# Gain 5 Block
# Deal 6 Damage, add a copy of this card to your discard pile
# Deal Damage equal to your Block
# Can only play if every other card in hand is an attack. Deal 18 Damage
# Deal 9 damage. Put a card from your discard pile on the top of draw pile
# Deal 6 damage. Deal 2 extra damage for ALL your cards containing "strike"
# Costs 1 less every time you take damage this battle. Deal 18 damage
# Card is exhausted if not played
# Deal 14 damage, strength affects this card 5 times
# Deal 10 damage, if fatal gain 3 max hp
# > Specific Card or Card Theme Ideas:
# Entire card synergy for gambling cards with varying output values
# Cards that deal damage based on gambles made this battle
# Luck stats to affect output ranges using biased die # < EXISTS
# Curse card that deals damage to you on some condition
# When removed from deck gain something based on damage taken total
# Allows you to gain a bonus based on how much it fucks you over
# Attack cards that work with dexterity
# Name: Bellow, Description: 'Daze the enemy for two turns. You draw to 10 cards next turn. End your turn.'
# name = 'Drain', description = 'Gain hp equal to half your block, remove all block.'
# name = 'Ashed to Ashes', description = 'Deal 6 damage, if this kills an enemy exhume a card.'
# name = 'Ritual Sacrifice', description = 'Lose 20 percent of max HP, gain 2 max HP permanently.'
# name = 'Tend to Wounds', description = 'Gain block equal to HP lost this battle.'
# > General Card Ideas
# Retain on cards can tick down each turn, eg. card has retain (3)
# Deal 8 damage, If there are damage multipliers, double them
# < Other Mechanics Ideas
# Many classes and objects owned by the player or card that trigger at the right times, eg. card has an object that says mana is now 2 less until played and this triggers on mana check and is removed on play
# ! Complicated Cards
# Deal 30 damage, only playable if there are no other attack cards in hand
# Deal 4 damage for each attack played with this card in hand. Retain(3)
# > Power Whenever you play a card deal 1 damage to all targets
# > Power Whenever you play a card gain 1 block
# > Power When an attack deals unblocked damage, apply 1 poison
# > Power Next turn your attacks deal double damage
# < Curse You cannot play more than 3 cards this turn
# < Curse Lose 1HP every time a card is played
# Deal 18 damage, can only be played if every card in hand is an attack card
# < Curse At the end of your turn lose HP equal to number of cards in hand
# Card that if it breaks block deals damage again, or plays again
# Deal 6 damage, gain hp equal to flesh damage dealt
# All attack cards in hand cost 1 more mana while this card is in hand
# This card applies strength value 5 times
# Deal 6 damage, if you have played less than 4 cards, draw a card
# Deal 10 damage, deal twice as much if the target has block
# ! Possible Issues
# Implementation of modifiers, such as deal double damage or a run modifier where you gain double gold, or a 20% chance to cause critical hit
# & Other Ideas
# Make cards allow gem style upgrades from monster train
# Ability to fix a mana value or add mana floor for a card
# Cursed relics, binding = cannot play more than 5 cards per turn
# Gems can be applied to cards to give persistent effects
# Card that has bonuses depending on number of gems
# Player ability - The next attack card applies strength an extra 4 times
# * General Ideas
# Non Conditional post effects
# Responsive affects trigger on event eg. taking damage triggers spikes
# Functions for dealing damage and applying affects - with the option to post the result to the event queue, or to avoid posting, as in the case of "when you deal damage, deal 1 more" to avoid infinite circular
# Affects may have delays, such as draw 2 cards at the start of next turn
# @ General Information:
# Skills in Slay the Spire never have a damage value
# Cards with playability conditions would apply these after normal conditions such as mana cost
# ! All cards should have their own function for damage, either inherited or set, such that they can deal damage based on criteria, and such that they can alter the rate at which they apply strength, or indeed in the case of some cards they may work with dexterity
# % Cards may need to check all relics, affects and cards in hand to check for bonus damage?
# Useful Files:
# G:/Coding/Python/card_game_messy/cards.py # < Cards with conditionals
# ! Other
# Action code to stack and have listeners that look for specific events
# System of event controllers - player_takes_damage, player_plays_card
# List of Actions:
# card_played, card_discared, card_exhausted, card exhumed, cards_drawn, deck_shuffled, hp_change, gold_change, strength_change, dexterity_change, block_change, mana_change, battle_started, turn_started, enemy_defeated, battle_completed, player_defeated, card_gained, card_removed, affect_gained, affect_removed, ability_gained, ability_removed, gem_gained, gem_removed, relic_gained, relic_removed, potion_gained, potion_used
# > Order of Play
# Check if playable
# Calculate Cost
# Pay cost (Mana and or Gold for instance)
################################################################################
####################### Other #########################
################################################################################
class History:
"""In order for some cards to work they need knowledge of what has happened this turn or this battle, this is an attempt at a near exhaustive list of useful counters, many would need to be split into [turn, battle, run] for different effects that are tied to them"""
cards_played = 0
block_damage = 0
block_broken = 0
cards_played = 0
attacks_played = 0
enemies_damaged = 0
times_taken_damage = 0
total_damage_taken = 0
self_damage = 0
hp_gained = 0
times_hp_gained = 0
affects_applied_to_enemies = 0
poison_applied_to_enemies = 0
poision_gained = 0
cards_discarded = 0
cards_exhausted = 0
cards_drawn = 0
draw_card_calls = 0
turn_number = 0
floor_number = 0
enemies_in_combat = 0
number_of_current_affects = 0
actions_taken_this_turn = 0
elites_slain = 0
gold_gained = 0
strength_gained = 0
dexterity_gained = 0
gambles_made = 0
curses_gained = 0
curses_exhausted = 0
common_cards_played = 0
rare_cards_played = 0
legendary_cards_played = 0
def cards_containing_string(string):
pass
def cards_in_hand():
pass
def curses_in_deck():
pass
def number_of_card_in_hand_by_type():
pass
def times_this_card_has_been_played():
pass
def most_recent_card_drawn_type():
pass
# > Create a keyword list
keywords = ['draw', 'play', 'discard', 'exhaust', 'apply', 'harvest']
################################################################################
###################### Paradigm ######################
################################################################################
import random
from copy import deepcopy
# ? Paradigm
# You are trying to play a skill, it has no target, you gain 5 block
# Whenever you play a card you take 1 damage
# Whenever you take damage you gain 1 strength
class Card:
def __init__(self):
"""Simple Card class"""
self.name = 'default'
self.damage = None
self.block = None
self.type = None
self.description = None
self.affects = None
self.actions = 1
self.mana = None
self.draw = None
def copy(self):
"""Return a full copy of all attributes and objects in card"""
return deepcopy(self)
def playable(self, player, target):
"""Card specific playability, to be called after regular checks"""
return True
# ~ Player Class with Shuffling Methods
class Player:
def __init__(self, name):
"""Player class with multiple attributes and methods"""
self.name = name
self.hp = 50
self.max_hp = 75
self.mana = 3
self.base_mana = 3
self.card_draw = 5
self.base_card_draw = 5
self.block = 10
self.relics = []
self.cards = {
'owned': [],
'draw': [],
'hand': [],
'discard': [],
'exile': []}
self.test_cards = {
'owned': [0,1,2,3],
'draw': [4,5,6,7,8],
'hand': [9,10,11,12,13,14,15],
'discard': [16,17,18,19,20],
'exile': [21,22]
}
self.affects = {
'invulverable': None,
'posion': None,
'reckless': None,
'taunt': None,
'slow': None
}
self.strength = 4
self.dexterity = 4
self.gold = 100
self.potions = {
'slot_1': None,
'slot_2': None,
'slot_3': None
}
self.experimental_variables = {
'times_taken_damage': 6,
'attacks_played_this_turn': 2
}
self.embargos = ['special']
self.card_draw = 5
self.immunities = ['rare']
def debug(self):
"""Messy but useful debugging function to print card lists"""
input('-----------------------------------------------------------')
print(f'Owned: {self.cards["owned"]} {len(self.cards["owned"])}')
print(f'Draw: {self.cards["draw"]} {len(self.cards["draw"])}')
print(f'Hand: {self.cards["hand"]} {len(self.cards["hand"])}')
print(f'Discard: {self.cards["discard"]} {len(self.cards["discard"])}')
print(f'Exile: {self.cards["exile"]} {len(self.cards["exile"])}')
def shuffle_cards(self):
"""Combine draw pile and player hand and shuffle the deck"""
self.cards['draw'] = self.cards['draw'] + self.cards['discard']
self.cards['discard'].clear()
random.shuffle(self.cards['draw'])
def start_battle(self):
"""Move cards from owned to draw and shuffle draw pile"""
self.cards['draw'] = self.cards['owned'][:]
random.shuffle(self.cards['draw'])
self.debug()
def cards_in_draw(self):
"""Return the number of cards in draw."""
return len(self.cards['draw'])
def cards_in_hand(self):
"""Return the number of cards in hand."""
return len(self.cards['hand'])
def cards_in_discard(self):
"""Return the number of cards in discard."""
return len(self.cards['discard'])
def draw_one_card(self):
"""Draw a single card, obey the limitations of hand size, shuffle cards if no cards in draw pile and there are cards in the discard pile."""
if self.cards_in_hand() >= 10:
print('You can only hold 10 cards in hand.')
elif self.cards_in_draw() > 0:
card = self.cards['draw'].pop()
self.cards['hand'].append(card)
elif self.cards_in_discard() > 0:
print('No cards left in draw pile, shuffling deck')
self.shuffle_cards()
card = self.cards['draw'].pop()
self.cards['hand'].append(card)
else:
print('There are no cards left to draw.')
self.debug()
def new_hand(self):
"""Move cards in hand to discard pile and draw a new hand"""
print('Drawing a new hand')
self.cards['discard'] = self.cards['discard'] + self.cards['hand']
self.cards['hand'].clear()
cards_to_draw = self.card_draw
for card in range(cards_to_draw):
self.draw_one_card()
def draw_cards(self, number_of_cards):
"""Draw cards according to the conditions of draw one card function."""
for number in range(number_of_cards):
self.draw_one_card()
def discard(self, card):
"""Discard the supplied card and move from hand to discard pile."""
self.cards['hand'].remove(card)
self.cards['discard'].append(card)
def exile(self, card):
"""Discard the supplied card and move from hand to graveyard."""
self.cards['hand'].remove(card)
self.cards['exile'].append(card)
def shuffle(self, deck):
"""Shuffle a deck of cards in list format to give random order list"""
random.shuffle(deck)
def aim_card_at(self, card, target):
"""Check playability of a card"""
if card.playable is not True:
return 'Unplayable due to conditional card restrictions.'
elif card.mana > self.mana:
return 'Unplayable due to card cost.'
elif card.type in self.embargos:
return 'Unplayable due to embargo.'
elif card.type in target.immunities:
return 'Unplayable due to target immunity.'
elif not card.playable(self, target):
"Card has failed a playability check"
def decision(self, target):
"""Simple player decision using text input"""
options = ['end_turn'] + self.cards['hand']
print(f"You have {len(options)} options to choose from:")
for index, option in enumerate(options):
if hasattr(option, 'name'):
print(f'[{index}] {option.name.title()} [Mana: {option.mana}] [Damage: {option.damage}]')
else:
print(f"[{index}] {option.replace('_', ' ').title()}")
valid = False
while not valid:
choice = int(input('Choose index value: '))
if 0 <= choice < len(options):
valid = True
decision = options[choice]
print('---------------------------------------------------------')
if hasattr(decision, 'name'):
print(f'You have chosen {decision.name}')
else:
print(f'You have chosen {decision}')
if decision == 'end_turn':
return True
else:
print('Card play function goes here')
def playable(self, card, target):
"""Player check for card playability"""
if card.mana > self.mana:
print('Unplayable due to card cost.')
return False
elif card.type in self.embargos:
print('Unplayable due to embargo on player.')
return False
elif card.type in target.immunities:
print('Unplayable due to target immunity.')
return False
else:
return True
def show_attributes(self):
"""Print all keys and values of the object's attribute dictionary"""
print('\n-------------------------------------------------------------')
for key, value in self.__dict__.items():
print(f'{key} = {value}')
print('-------------------------------------------------------------\n')
class SignatureMove(Card):
def __init__(self):
"""Example card from Slay the Spire with conditional playability"""
super().__init__()
self.name = 'Signature Move'
self.type = 'attack'
self.description = 'Deal 40 damage, can only be played if this is the only attack card in hand.'
self.mana = 2
# def playable(self, player, target):
# """Check the player hand for attack cards, excluding this card, and if there are no other attack cards then the card is set to playable."""
# hand = player.cards['hand']
# attacks = [card.type == 'attack' for card in hand if card is not self]
# if not any(attacks):
# print("There are no other attack cards in hand.")
# return True
def playable(self, player, target):
"""Check if this card is playable in the current situation"""
hand = player.cards['hand']
attack = lambda card: card.type == 'attack' and card is not self
return not any(attack(card) for card in hand)
###################### Tests #########################
def test_signature_move():
"""Will return False if there are other attacks in hand"""
player = Player('Adventurer')
signature_move = SignatureMove()
other_attack_card = SignatureMove()
normal_card = Card()
player.cards['hand'] = [signature_move, other_attack_card, normal_card]
print(list(card.type for card in player.cards['hand']))
player.debug()
print()
print(signature_move.playable(player, None))
def test_text_decision():
player = Player('Adventurer')
enemy = Player('Goblin')
signature_move = SignatureMove()
other_attack_card = SignatureMove()
normal_card = Card()
player.cards['hand'] = [signature_move, other_attack_card, normal_card]
player.decision(enemy)
def check_playable():
player = Player('Adventurer')
enemy = Player('Goblin')
signature_move = SignatureMove()
other_attack_card = SignatureMove()
normal_card = Card()
player.cards['hand'] = [signature_move, normal_card]
player_playable = player.playable(signature_move, enemy)
card_playable = signature_move.playable(player, enemy)
playable = card_playable and player_playable
print(playable)
# def damage(entity, value, announce = True):
# """Here the argument for announce would allow for an initial damage figure that triggers a secondary damage, secondary damage would not trigger announcement and avoid a circular loop. This would allow for things such as every time you deal damage, deal 1 more. Though it may be more complex for things such as the next attack deals double damage which would definitely not be applied in post"""
# pygame_events = []
# entity.hp -= value
# if announce:
# pygame_events.append({entity, value})
^$SECTION
myprocessor_notes.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
''' => Notes =============================================================================================
Attempt to combine and improve upon previous attempts at making a processor
=> =================================================================================================== '''
class Processor: ...
class Tree: ...
class Event: ...
class Resolver: ...
class Intention: ...
class Action: ...
class Subaction: ...
class Subintention: ...
class Node: ...
class Payload: ...
class Pathway: ...
class Request: ...
class Cascade: ...
class EventCascade: ...
class Alteration: ...
class Modification: ...
class EventCascade: ...
class EventBus: ...
class Mutation: ...
class Rule: ...
class Augmentation: ...
def populate(): ...
def process(): ...
def recurse(): ...
def preprocess(): ...
def postprocess(): ...
def evaluate(): ...
def enact(): ...
def transact(): ...
def prune_pathways(): ...
def prune(): ...
def get_obstructions(): ...
def process_obstructions(): ...
def process_callables(): ...
def get_subintentions(): ...
def process_request(): ...
def send(): ...
def subscribe(): ...
def handle(): ...
def get_actions(): ...
def update_responses(): ...
def respond(self, request: Request): ...
def process_request(self, request: Request): ...
def process_event(self, event: Event): ...
executor = None
source = None
recipient = None
parent = None
class item: ...
item.before
item.after
item.during
item.postrequests
item.prereqiests
item.corequests
''' => Notes =============================================================================================
def process(request: Request):
update_responses(request)
obstructions: list[Obstruction] = []
modifiers: list[Modifier]
before: list[Request] = []
after: list[Request] = []
for request, timing in request.responses:
if type(request) == Obstruction:
obstructions.append(request)
elif type(request) == Modifier:
modifiers.append(request)
elif timing == "before":
before.append(request)
elif timing == "after":
after.append(request)
=> =================================================================================================== '''
''' => Notes =============================================================================================
Tags / Flags to alter how some actions / intentions work
=> =================================================================================================== '''
''' => Notes =============================================================================================
class Request:
def __init__(self) -> None:
"""Initialise a request"""
class Functional(Request):
"""Class for higher-order functional requests to be executed during processing"""
def __init__(self, func: callable, *args: list[any], **kwargs: dict[any, any]) -> None:
"""Initialise a functional object with a stored function / method and any arguments"""
self.func: callable = func
self.args: list[any] = args
self.kwargs: dict[any, any] = kwargs
def __call__(self) -> any:
"""Call the stored function / method and pass any stored arguments"""
return self.func(*self.args, **self.kwargs)
class other:
def increment(self, attribute: str, change: int | float) -> None:
"""Increment a numerical attribute"""
current: int | float = getattr(self, attribute)
new: int | float = current + change
setattr(self, attribute, new)
=> =================================================================================================== '''
^$SECTION
- Deal 8 damage
- Deal 8 damage, gain health equal to the unblocked damage dealt
- Deal 8 damage two times
Deal 8 damage, draw a card
- Gain 8 block
Gain 8 block, deal damage equal to your block
- Deal 8 damage (you have 4 strength)
Deal 8 damage, strength affects this card 5 times (you have 4 strength)
- Deal 8 damage, if this deals flesh damage, draw two cards
Deal 8 damage, draw a card
- Deal 8 damage, draw a card, (discard self)
Componenets, order of operations
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class HeavyStrike(Card):
name: str = "heavy strike"
rarity: str = "uncommon"
category: str = "attack"
owner: Entity
value: int
mana: int
quirks: list[Quirk]
def __init__(self, owner: Entity, value: int = 12, mana: int = 2):
"""Initialise a card"""
self.owner: Entity = owner
self.value: int = value
self.mana: int = mana
self.quirks: list[Quirk] = [_quirk.Discards(self), _quirk.Heavy(self)]
def process(self, request: Request, quirks: list[Quirk]) -> None:
"""Process a request involving this card"""
request.add(_request.ApplyDamage(self.owner, request.recipient, self, self.value), "during")
@property
def description(self):
"""Get current card description"""
return f"Deal {self.value} damage." + super().description
Strike Defend Reap
Multi-Strike Heavy-Strike
OR
Multi-Play Heavy-Card
Say you heavy gem a card, all actions on the card are heavy, and it costs 2 more?
class tags: AFTER
Discards Exhausts: tags.AFTER
game.quirk.discards game.quirk.exhausts game.quirk.heavy game.quirk.reaping
class CardRequest
1
2
def _(request, quirks, afters):
pass
^$SECTION
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
# ~ ======================================================================================================
# ~ GameObject Class
# ~ ======================================================================================================
class GameObject:
"""Main parent class to extend functionality to subclasses"""
tags: list[str] = []
name: str = "name"
priority: int = -99
flavour: str = "flavour"
active: bool = True
description: str = "description"
def __init__(self, *args, **kwargs) -> None:
"""Default initialisation for a game object"""
for key, value in kwargs.items():
setattr(self, key, value)
def __repr__(self) -> str:
"""Default string representation of game object with shortened ID"""
uid: str = str(id(self))[-4:]
name: str = self.__class__.__name__
return f"{name} #{uid}"
# ~ ======================================================================================================
# ~ Entity Class
# ~ ======================================================================================================
class Entity(GameObject):
"""Parent Entity class to extend functionality to Entity subclasses"""
def __init__(self):
"""Placeholder entity initialization"""
self._health: int = 5
self._max_health: int = 10
self._block: int = 5
self._gold: int = 5
self._energy: int = 5
self._potions: list = []
self._relics: list = []
self._traits: list = []
self._effects: list = []
@property
def health(self):
"""Get the health attribute"""
return self._health
@health.setter
def health(self, value):
"""Set the health attribute"""
if value != self._health:
print(f"{self}: Health changed from {self._health} to {value}")
self._health = value
@property
def max_health(self):
"""Get the max_health attribute"""
return self._max_health
@max_health.setter
def max_health(self, value):
"""Set the max_health attribute"""
if value != self._max_health:
print(f"{self}: Maximum Health changed from {self._max_health} to {value}")
self._max_health = value
@property
def block(self):
"""Get the block attribute"""
return self._block
@block.setter
def block(self, value):
"""Set the block attribute"""
if value != self._block:
print(f"{self}: Block changed from {self._block} to {value}")
self._block = value
@property
def gold(self):
"""Get the gold attribute"""
return self._gold
@gold.setter
def gold(self, value):
"""Set the gold attribute"""
if value != self._gold:
print(f"{self}: Gold changed from {self._gold} to {value}")
self._gold = value
@property
def energy(self):
"""Get the energy attribute"""
return self._energy
@energy.setter
def energy(self, value):
"""Set the energy attribute"""
if value != self._energy:
print(f"{self}: Energy changed from {self._energy} to {value}")
self._energy = value
@property
def potions(self):
"""Get the potions list attribute"""
return self._potions
@property
def relics(self):
"""Get the relics list attribute"""
return self._relics
@property
def effects(self):
"""Get the effects list attribute"""
return self._effects
@property
def traits(self):
"""Get the traits list attribute"""
return self._traits
def damage(self, value) -> list["Request"]:
""""""
print(f"{self} being damaged for {value}")
damage = value
starting_block = self.block
self.block = max(0, starting_block - damage)
flesh_damage = max(0, damage - starting_block)
self.health -= flesh_damage
return locals()
def heal(self, value) -> list["Request"]:
""""""
print(f"{self} being healed for {value}")
self.health += value
return None
# ~ ======================================================================================================
# ~ Other Classes
# ~ ======================================================================================================
class Item(GameObject):
pass
class Request(GameObject):
status: str = "void"
class Play(Request):
def __init__(self, card, recipient, callbacks = []) -> None:
"""Initialise a play request"""
self.card = card
self.recipient = recipient
self.callbacks = callbacks
def on_success(self) -> list[Request]:
"""Get subrequests from successful request"""
return self.card.play(self.recipient)
class Damage(Request):
def __init__(self, value, recipient, callbacks = []) -> None:
"""Initialise a damage request"""
self.value = value
self.recipient = recipient
self.callbacks = callbacks
def on_success(self) -> list[Request]:
"""Get subrequests from successful request"""
return self.recipient.damage(self.value)
class Heal(Request):
def __init__(self, value, recipient, callbacks = []) -> None:
"""Initialise a heal request"""
self.value = value
self.recipient = recipient
self.callbacks = callbacks
def on_success(self) -> list[Request]:
"""Get subrequests from successful request"""
return self.recipient.heal(self.value)
class Card:
def __init__(self, owner) -> None:
"""Initialise a card item"""
self.owner = owner
self.value = 1
self.category = "default"
def play(self, recipient) -> list[Request]:
"""Play this card and return any requests"""
return []
class Strike(Card):
def __init__(self, owner) -> None:
"""Initialise a Strike card"""
self.owner = owner
self.value = 6
self.category = "attack"
def play(self, recipient) -> list[Request]:
"""Play this card and return any requests"""
return [Damage(self.value, recipient)]
class HealingStrike(Card):
def __init__(self, owner) -> None:
"""Initialise a HealingStrike card"""
self.owner = owner
self.value = 12
self.category = "attack"
def play(self, recipient) -> list[Request]:
"""Play this card and return any requests"""
callbacks = [{
"style": Heal,
"value": lambda result: result["flesh_damage"],
"recipient": self.owner,
}]
return [Damage(self.value, recipient, callbacks = callbacks)]
# ~ ======================================================================================================
# ~ Processing Functions
# ~ ======================================================================================================
def get_request_from_callback(callback: dict, result: dict):
"""Construct request from a callback dictionary and result"""
constructor = callback["style"]
del callback["style"]
parameters = {key: value(result) if callable(value) else value for key, value in callback.items()}
return constructor(**parameters)
def process(request):
"""Process request, subrequests, and callbacks"""
request.status = "success"
result = None
if request.status == "success":
result = request.on_success()
elif request.status == "fail":
result = request.on_fail()
elif request.status == "null":
result = request.on_null()
if result and not isinstance(result, dict):
print(f"Subrequests found for {request}")
for subrequest in result:
print(f" {subrequest}")
process(subrequest)
elif request.callbacks:
print(f"Callbacks found for {request}")
for callback in request.callbacks:
print(f" {GameObject.__repr__(callback)}")
callback_request = get_request_from_callback(callback, result)
process(callback_request)
# ! ======================================================================================================
# ! Testing
# ! ======================================================================================================
player = Entity()
enemy = Entity()
card = HealingStrike(player)
request = Play(card, enemy)
process(request)
^$SECTION
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
""" => Notes =============================================================================================
Request system - Make a request
You attempt to play an attack card, it is nullified by entangle - you CANNOT play it
You attempt to play an attack card, it is obstructed by entangle - it is played with no effect
Play is the action and it must decide what happens depending on its status code
=> =================================================================================================== """
def get_pathways(element, observers) -> list[list]:
"""Get all pathways from a root element as a list of lists"""
all_pathways = []
def populate_pathways(element, observers, current_pathway):
"""Populate pathways recursively where obstructions occur"""
obstructions = []
for observer in observers:
if type(observer) is type(element):
continue
elif observer in current_pathway:
continue
elif observer.obstructs(element):
obstructions.append(observer)
if len(obstructions) == 0:
all_pathways.append(current_pathway)
elif len(obstructions) == 1:
obstruction = obstructions[0]
current_pathway.append(obstruction)
populate_pathways(obstruction, observers, current_pathway)
else:
for obstruction in obstructions:
new_pathway = current_pathway + [obstruction]
populate_pathways(obstruction, observers, new_pathway)
populate_pathways(element, observers, [])
return all_pathways
class Request:
pass
class Damage(Request):
status = "void"
def __init__(self, value, callbacks = []) -> None:
self.value = value
self.callbacks = []
def get_actions(self, recipient) -> list:
actions = []
if self.status == "success":
recipient.damage(self.value)
elif self.status == "failure":
pass
elif self.status == "void":
pass
return actions
class Heal(Request):
status = "void"
def __init__(self, value, callbacks = []) -> None:
self.value = value
self.callbacks = []
def get_actions(self, recipient) -> list:
actions = []
if self.status == "success":
recipient.heal(self.value)
elif self.status == "failure":
pass
elif self.status == "void":
pass
return actions
class Discard(Request):
status = "void"
def __init__(self, card, callbacks = []) -> None:
self.card = card
self.callbacks = []
def get_actions(self) -> list:
actions = []
if self.status == "success":
self.card.source.discard(self.card)
elif self.status == "failure":
pass
elif self.status == "void":
pass
return actions
class Play(Request):
status = "void"
def __init__(self, card, callbacks = []) -> None:
self.card = card
self.callbacks = []
def get_actions(self, recipient) -> list:
actions = []
if self.status == "success":
self.card.play(recipient)
elif self.status == "failure":
pass
elif self.status == "void":
pass
return actions
class Double:
"""Activates twice if you are below 50% health"""
def get_actions(self) -> list:
actions = []
if self.status == "success":
action = Play(self)
actions.append(action)
elif self.status == "failure":
pass
elif self.status == "void":
pass
return actions
def play(self, status):
actions = []
if status == "success":
actions.append(Damage(self.value))
if self.owner.health <= self.owner.max_health / 2:
actions.append(Damage(self.value))
actions.append(Discard(self))
class CardTest:
def nesting(self, recipient):
{
"request": Play,
"parameters": {
"card": self,
"recipient": recipient,
"callbacks": {
"request": Damage,
"parameters": {
"value": self.value,
"recipient": recipient,
"callbacks": {
"request": Heal,
"args": ["flesh_damage", self.source],
"callbacks": None
}
}
}
}
}
def nesting(self, recipient):
{
"request": Play,
"parameters": {
"card": self,
"recipient": recipient,
"callbacks": {
"request": Damage,
"parameters": {
"value": self.value,
"recipient": recipient,
"callbacks": {
"request": Heal,
"parameters": {
"recipient": self.owner,
"value": lambda result: result.value
},
"callbacks": None
}
}
}
}
}
def test(self, recipient):
request = Play(self.card)
request.add_callback()
Nested(
request = Play,
card = self,
recipient = recipient,
callbacks = [
Nested(
request = Damage,
value = self.value,
)
]
)
class Player:
pass
player = Player()
def update_status(request):
pass
class Rest(Request):
status = "success"
results = None
def get_subrequests(self):
{
"flavour": Heal,
"value": self.recipient.max_health * 0.15,
}
request = Rest(player)
update_status(request)
subrequests = request.get_subrequests()
def process(request_dictionary, result = None):
"""Process all requests and callbacks from a given request dictionary"""
request = request_dictionary["request"]
parameters = request_dictionary["parameters"]
callbacks = parameters["callbacks"]
finalised = {}
if result is not None:
for key, value in parameters.items():
if callable(value):
finalised[key] = value(result)
else:
finalised[key] = value
else:
finalised = parameters
class Package:
def __init__(self, flavour, parameters):
self.flavour = flavour
self.parameters = parameters
def get_request(self):
def get_value(self, key):
pass
parameters = ...
def create_request(flavour, parameters, callbacks, result = None) -> Request:
"""Process all requests and callbacks from a given request dictionary"""
if result is not None:
for key, value in parameters.items():
if callable(value):
parameters[key] = value(result)
request = flavour(**parameters)
result = request.get_result()
for callback in callbacks:
create_request(callback.flavour, callback.parameters, callback.callbacks, result)
return request
# Player requests Play card against enemy
# assess if it passes
# assess all subsequemt reqiests
class Nested:
def __init__(self, request, parameters = None, callbacks = None):
self.request = request
self.parameters = parameters or {}
self.callbacks = callbacks or {}
def execute(self):
result = self.request(**self.parameters)
for callback_name, callback_data in self.callbacks.items():
callback = callback_data["request"](**callback_data.get("parameters", {}))
result[callback_name] = callback.execute()
return result
lambda result: result.value
class Action:
def __init__(self, flavour, callbacks):
pass
# Each card will play in a different way
# Generally attempt a card play, if success then do action and discard, if fail do discard, if void do nothing
# thing: arguments, callbacks {thing: arguments}
class Request:
pass
class Entity:
pass
class Play(Request):
status = "void"
def __init__(self, source, card, recipient) -> None:
self.source = source
self.card = card
self.recipient = recipient
def on_success(self) -> list[Request]:
return self.card.play(self.recipient)
class Damage(Request):
status = "void"
def __init__(self, value, recipient) -> None:
self.value = value
self.recipient = recipient
def on_success(self) -> list[Request]:
self.recipient.damage(self.value)
class Card:
def __init__(self, owner) -> None:
self.owner = owner
self.value = 5
self.category = "attack"
def play(self, recipient) -> list[Request]:
"""Play this card by requesting Damage against """
requests = [Damage(self.value, recipient)]
def core(request):
request.status = "success"
if request.status == "success":
subrequests = request.on_success()
elif request.status == "fail":
subrequests = request.on_fail()
elif request.status == "null":
subrequests = request.on_null()
else:
raise UserWarning("Request staus not succces / fail / null")
for subrequest in subrequests:
core(subrequest)
player = Entity()
enemy = Entity()
card = Card()
request = Play(player, card, enemy)
^$SECTION
Aim is to make a second phase passing through player attributes and relics, and enemy attributes to check playability and attribute changes
Second aim is to creat a post card affects such as “if block broken”
> Player plays Card either at Player or at Enemy, the Card has conditions to change its attributes based on the battle state, its own attributes, Player attributes or Enemy attributes - Player affects such as “Vigour, add 8 damage to your next attack.” will then compound the attributes of the Card, alongside strength and strength multipliers, damage multipliers will be pooled together such as vulnerable, invulnerable, reckless etc to alter the overall damage value, once all this is done the card will attempt to be played - here any affect immunities or vulnerabilities will be realised such as being immune to reckless or vulnerable and as such the card in this case would be successful but the effects would be unsuccessful. In the special case of dodge the card would be considered failed and would be discarded with no effect as the entity would have dodged this source of damage - or the next offensive action.
> Only a successful card play should trip a relic, as counters may be reset - therefore playability should be assessed as soon as possible and the criteria should be decided - a card may not necessarily do exactly what it wants to do but that is not the success criteria, a successful card could then be assessed to see if it puts out any meaningful data (damage, affects etc.) but in any case a successful attack play for instance would still increase an attack counter
< Unplayable
1
# < Card conditions, mana cost too high, the card type has been embargoed, the player has a card limit per turn, this target is unable to be targeted at this time
< Played and failed
1
# < Card is dodged, card misses the target, card is playable but the effects are conditional and did not succeed such as "judgement"
Card in Hand - On Play Effects
1
2
3
# When played reduce all cards in hand to 1 mana for the battle
# > Or keyword "Fix the mana of all cards in hand at 1"
# Event that fixes the mana of a card at a random value 1-3
Card in Hand - Passive Effects
1
2
# All attack cards in hand cost 1 more mana while this card is in hand
# > Or lose 1 mana every time you play an attack card
Give a card “play twice” and “exhaust”
keywords = [‘draw’, ‘play’, ‘discard’, ‘exhaust’]
^$SECTION
Make a request to play, check validity, then make a request for damage and a request to draw cards, then deal with these separately
Card posts itself to the request system with a target
Card must have its own methods that are to be used when passed into the request system
A card that first checks player block, then deals damage equal to it will need a method so that the request system can check if the card is playable, and once playable it will do these calculations
The results of a played card will need to be stored
The request could be a pygame user event with all the info, and this could be processed from the event queue to create a new event - though that occurs across two frames
& Pass in a card request - Check Status Effects, Card Parameters, Relic Triggers - Play Card, Update Damage, Update Status Effects, Update Relics and check for effects after the card is played
PARADIGM:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# RELIC: Your third attack each turn deals double damage
# RELIC: When deal flesh damage to an enemy, gain 1 HP
# RELIC: When you gain HP gain an equal amount of gold pieces
# RELIC: Attacks which hit multiple times deal 5 extra damage per attack
# CARD: Double Slap: Deal 8 damage 2 times to one enemy
# CARD: Gilded Fist: Deal damage equal to your gold
# CARD: Kick Them While they're Down: Deal 18 damage, can only be played when two other attack cards have been played this turn
# CARD: Finisher: For every attack card played while this card was in hand, deal 5 damage
# CARD: Sacrifice: Lose 10 percent HP, gain 4 mana
# CARD: When a card in your hand is played, lose 3HP
# CARD: All cards in your hand cost one less mana, or an action that a card drawn into your hand with this card costs one less mana... or you gain 1 mana for every card you play, when this card is played randomise the cost of all cards in hand for the remainder of the battle and exhaust the card
# ? Implies card status effects, essentially given a mana token from this card, and could get uniquie other ones from other cards or relics or upgrades, every tick check that every card in your hand has your specific mana token
# ? Addition then multipliers such as add 5 damage then do double damage
# Card would need a start turn thing, reduce all by 1 mana, and it would reduce mana of any cards coming into your hand, these affects would last one turn
# > All cards therefore have an attribute for mana THIS TURN
# Pre play card method, so that a card or relic that affects damage or mana or something combines with the card that is being played to alter stats
# PLAYER STATUS: RECKLESS: Deal double damage, 33 percent change of damaging self instead
# ENEMY STATUS: BIDE: Take 50% less damage this turn, all damage taken this turn will be reflected to the player next turn
# BATTLE STATUS: EMBARGO: Special cards are embargoed
Card in Hand - On Play Effects
1
2
3
# When played reduce all cards in hand to 1 mana for the battle
# > Or keyword "Fix the mana of all cards in hand at 1"
# Event that fixes the mana of a card at a random value 1-3
Card in Hand - Passive Effects
1
2
# All attack cards in hand cost 1 more mana while this card is in hand
# > Or lose 1 mana every time you play an attack card
Give a card “play twice” and “exhaust”
keywords = [‘draw’, ‘play’, ‘discard’, ‘exhaust’]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
### !!! Should attribute changes include player strength and dexterity adjustments to account for a card that adds double strength for instance
# > How does that apply if you had a relic that says strength is applied twice this card etc - would they conflic, should there be a strength multiplier, card strength modifier + player strength modifier + relic strength modifier etc
# > A relic that applies twice the poision
# Card
# Card is copied
# Card checks its conditions to update the copy
# Card is returned
# Player
# Player strength and dexterity are applied based on card/player/relics
# Player affects and relic affects alter values
# Card is returned
# Other:
# The result of the card is realised and applied
# Result is returned
# Card
# Card checks its conditions based on the results
# Card applies a post effects on target eg. damage, poison, etc.
> For a cards that is based on total attacks played this battle to work it will need access to the stat required, either for a card that scales per card, or for a card that can only be played once 10 attack cards have been played
1
# ? It could request the "battle" object to find the stat
? Every card played must be chronicled, and the affect of it must also be noted down - such as if it failed, did damage, healed the player, killed an enemy, broke block etc
> Consider a type of card that does not simply have an effect when played, it has a persistent effect - meaning it either functions as an affect the player has, an effect on cards in hand before, during or after they are played. Or functions as / affects relics the player posesses. These would be seen as persistent temporary effects - such as the pain curse that causes you to take damage when you play a card, and a card that reduces mana cost of all cards in hand while in hand, or the variant of the finisher that does not look at cards played this turn, but instead it counts up its own counter of cards played while it was in your hand (maybe it goes up forever, maybe it halves each turn or some decay rate - this would be an out of hand card affect)
If you draw two or more of the same card, gain 6 block, or double their stats or some shit
> Event
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# ? Pass Event Through Relic List to Check Event Type and Update Counters
# > Card(s)
# < Played
# & Success or Failure to Play
# & Card Object
# & Player or Enemy Target
# ? Player Relics ?
# < Draw Action
# & Success or Failure to Draw a Card
# < Drawn
# & Player Hand
# < Shuffled
# < Discarded
# < Exiled
# < Destroyed
# < Retained
# < Duplicated
# < Upgraded
# < Disabled
# < Stolen
# < Gained
# > Battle
# < Battle Start
# < Turn Start
# < Turn End
# < Battle End
# < Player Damaged
# < Player Buffed
# < Enemy Damaged
# < Enemy Buffed
# < Health Gained
# < Flesh Damage Done
# < Block Broken
# < Enemy Killed
# < Player Killed
# > Other
# < Relic Activated
# < Potion Used
# < Gold Gained
# < Gold Lost
# < Potion Gained
# < Relic Gained
^$SECTION
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# Overview:
# List possible class names and attribute names to be used
class Agent:
pass
class Governor:
pass
class Entity:
pass
class Card:
flavour = None
class Blueprint:
pass
class Packet:
pass
class Data:
pass
class Proposal:
pass
class Event:
pass
class Listener:
pass
class Subscriber:
pass
class Request:
pass
class Component:
pass
class Composite:
pass
class Tool:
pass
class Ticket:
pass
class Queue:
pass
class EventBus:
pass
class Flavour:
pass
class Modifier:
style = None
class Meta:
pass
class Void:
pass
class Alteration:
pass
class Nullifier:
pass
class Resolver:
pass
class Overseer:
pass
# Other Keywords:
# executor, source, recipient
class Damage:
pass
class HPLoss:
pass
class BlockGain:
pass
class BlockLoss:
pass
class BlockShed:
pass
class EffectGain:
pass
class HPLossTicket:
def __init__(self, value, entity):
"""Generate a ticket for entity hp loss"""
self.value = value
self.entity = entity
def enact(self):
pass
^$SECTION
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
"""
Possible Card Data
+ Damage
+ Block
+ Cost, mana or otherwise
+ Effect, applied to enemy or to self
+ Actions, or number of uses
+ Modifiers that may apply to other things while in hand
- 'while in hand all damage is doubled'
+ Modifiers that may apply to itself
+ - 'Deal double damage if you have less than 40% HP'
+ - 'Apply strength 5 times.'
+ Listeners that look for other things going on
+ - 'discard this card if you take damage'
+ Listeners that look for the result if this card is played
- 'Deal 6 damage. If you break an enemy's block, draw a card.'
- 'Exhaust on play'
+ Listeners that look for end of turn
- 'Exhaust if not played'
+ Rarity
+ Type
+ Faction
+ Name
+ Owner
+ Upgrade status
+ Upgrade paths
+ Targeting system
- Possible targets
- Quantity of targets
+ Miscellaneous
- Ignores block or deals 'True Damage'
General Notes
* Subscriptions to events could be posted and given a unique id, that way when the subscriber is notified of the result they can check that id
Request Structure
Card makes request to play
- Target is who the card aims at, executor is the card owner
Game checks if the request is valid
- If not the request fails
- If it is valid then the request is assessed
- Check what the card is requesting and disseminate requests
For instance a card may request to deal 8 damage and apply 2 vulnerable to a target in that order
The card request is made, the card request is validated, and then the request for 8 damage and the 2 vulnerable are sent in that order
The request for 8 damage is passed back to the Game, the game assesses what modifiers and nullifications there might be, for instance the request for 8 damage might be against an enemy that cannot take damage this turn, in that case the request fails, alternatively if the request does not fail there may be other modifiers, does the target take 50% less damage this turn, does the owner of the card deal 25% more damage this turn, etc
Once the contextual modifiers are applied then a request is made for the target to take damage, the Game could handle a request for a target to take damage, so that all entities take damage in the same way, where targets have block and hp and block is removed first, then hp, therefore a request for block loss may occur and similarly a request for hp loss could occur, these would also look for modifiers, maybe the enemy is only able to lose a maximum of 15 hp per turn for instance, or does not take overspill damage if it has block eg. if it takes 20 damage and has 15 block it loses all block but takes no hp damage. Once these requests are finalised they would all be finalised, as in the target would take the hp or block loss and then it would announce to the Game that it had done so, and all listeners to that event would be passed the information eg. the player may gain 1 gold for every time it deals damage to a target this turn, or the player may gain HP equal to HP damage it inflicts on others this turn... in this regard the player would be passed information about the result of the card play and then it would make new requests based on this result, for instance a request to gain HP, and that request would have to be checked by the Game for modifiers eg. gain 50% more HP when gaining HP, or you cannot gain HP this turn, etc. and once modifiers are applied then that value of HP would be applied to the player, creating a new announcement to listeners, for instance an enemy might have if the player gains HP, gain 1 strength, and the requests go on until a request is announced that no listeners are interested in
Same sort of thing applies for the request to apply 2 vulnerable
~ Mechanics
~ Add 2 to all enemies' weak and vulnerable
- This therefore applies nothing if the target has no weak or vulnerable
~ Sacrifice. You must first exhaust a card in order to play this.
~ Steal 14 block from the enemy.
~ Whenever a card is exhausted while this card is in your hand, gain 4HP.
Modifers:
- Integer modifiers
- Functional modifiers
- Alter the general flow of the game and have to make their own requests
- Eg. Temporary HP:
- If you had 8 temporary hp then you would not take hp loss until this is depleted, but additionally you would need to reduce your temporary hp functionally
- Contexts
- hp_loss
~ Tungsten Rod: Reduce hp_loss by one each time it occurs
~ Invincible: You can only lose X hp this turn
- Functionality: Must deplete stacks of invincible
~ Runic Cube: Whenever you have hp_loss draw a card
- Functionality: Must occur after a non-zero hp_loss and notify Runic Cube / Player to draw card
$ Whenever you take flesh damage from an attacker deal 6 damage back.
Other:
play the previous card again
Powers:
Rotten: Deal N poison damage whenever a card deals unblocked damage.
Bolster: Whenever you gain strength, gain one more.
Whenever you:
gain HP gain one more.
play an card deal 1 damage to all enemies.
play a skill card, gain 1 block.
are attacked deal N damage to the attacker.
draw a status card, deal N damage to all enemies.
draw a curse card, gain N health.
You can now:
* not gain health.
* not rest at bonfires.
* only target the enemy with the highest hp
* only play 5 cards per turn
* not play attacks.
* not play skills.
* not play 0 cost cards.
* not draw any cards.
$ no longer lose block at the end of your turn.
~ only lose 15 block at the end of your turn.
~ only lose N health this turn.
~ play status cards.
~ play unplayable cards.
~ play curse cards.
~ optionally spend one extra mana per turn, to do so you must spend 6 health.
~ Reserve Mana:
Persists across turns
Is spent by X-Cost cards
Should be checked when looking for if a card is playable
Some cards may use reserve only
Keywords:
Occult: This card can be played disregarding playability checks. [Gemstone effect on Grand Finale for instance]
Retain: This card is not discarded at the end of the turn.
Entity Keywords:
Bide: If damage would kill this entity, it remains on 1HP, remove Bide.
Thick Hide: Take N less damage each time you would take damage.
Resilient: The maximum damage you can take from a single hit is N.
Valuable: If overkilled by N or more, gain gold.
Functions:
Draw: Draw a card from your hand.
- Pick a card manually, draw from top or bottom, draw randomly
Draw Discard: Draw a card from your discard pile.
- Pick a card manually, draw from top or bottom, draw randomly
Draw Exile (Exhume): Draw a card from your exile cards.
- Pick a card manually, draw from top or bottom, draw randomly
End Turn: After playing this card, end your turn.
Take Another Turn: After playing this card, end your turn and take another turn.
Effects
Cleave: Apply effect of the card to all enemies.
Kill: Kill an enemy, regardless of HP.
Unknown: Increase the damage or block of the next valid card by N.
Judgement: If an enemy has N or less HP, kill it.
Cantrip: On draw, do something.
Mercy: This card cannot kill an enemy.
Absorb: Gain HP equal to the unblocked damage dealt.
Rotten: Deal N poison damage whenever this card deals unblocked damage.
Cards:
Mercy: Deal 40 damage. If this card would kill the enemy, instead discard it.
Downfall - Flip Out - Deal 11 damage, whenver you would gain block this turn, instead deal that much damage to all enemies.
Block Scenario:
Card: Defend for 8 block.
Frail: You receive 40% less block this turn.
Dexterity: You receive 1 extra block.
Terrfied: You cannot gain block this turn.
End of Turn Scenario:
Information: You have 5 cards in your hand.
Normal Behaviour: You discard your hand at the end of your turn, unless a card has the retain keyword.
Positive Modifiers:
Professional: You do not discard your skills at the end of your turn.
Tactical: You only discard 3 cards at the end of your turn.
Negative Modifiers:
Unprepared: You discard all cards with retain at the end of your turn.
** Notes
Temporary HP would be an added ruleset for taking damage
Reserve mana would be an added ruleset for end of turn mana loss, or just end turn in general
Buffer to prevent damage could be a ruleset for taking flesh damage on an integer modifier
Prevent the next time you would die. - Or next time you die revive with 40% HP, would hijack a death call, subscribe to death
"""
contexts = [
'hp_loss_nullification',
'hp_loss_calculation',
'hp_loss_finalisation',
]
# Postit - Notes from other sources below -
"""
- Entities
- Cards
- Components
- List of Component Objects
- component.get_actions(state)
- component.get_stacks(state, recipient)
- component.get_recipients(state, target)
- component.get_focus(state, target)
- component.get_effect(state, target)
- Cost
- Single Component Style Cost Object
- cost.get_stacks(state)
- cost.get_resource(state)
So if an entity in my game has cards, each card is initialised as Card(entity) and each card has a list of components, and each are initialised as Component(card)
I have this system so that components and cards can access information about their "owner", or in the case of the components their "owner's owner"
This system of referencing other objects seems a little clunky but I feel it may be the only way, or perhaps the most elegant despite this
Event Systems: Implement an event system to allow communication between different parts of your game without direct references. Components and cards could broadcast events, and other interested components or systems could listen for those events. This can help decouple the components and make your system more flexible.
Entity-Component-System (ECS) Architecture: Consider adopting an ECS architecture, which is designed to handle complex relationships between entities, components, and systems. In ECS, entities are essentially just IDs, and components are added to them. Systems process entities with specific components. This architecture can be more efficient and flexible for managing game entities and their behaviors.
Dependency Injection: If you're using an object-oriented programming language, you could utilize dependency injection to provide necessary references to components and cards when they are created. This can help manage the relationships more cleanly and avoid tight coupling.
Design Patterns: Look into design patterns like the Observer pattern or the Mediator pattern. These patterns can help manage communication between objects without direct references, making the system more maintainable and adaptable.
"""
class Ability:
def __init__(self, owner):
"""Instantiate ability"""
self.owner = owner
def on_card_play(self): ...
def on_condition_met(self): ...
def on_damage_incoming(self): ...
def on_damage_outgoing(self): ...
def on_damage_resolve(self): ...
def on_effect_incoming(self): ...
def on_effect_outgoing(self): ...
def on_turn_start(self): ...
def on_turn_end(self): ...
def on_counter_end(self): ...
def on_battle_start(self): ...
def on_battle_end(self): ...
"""
tags = [
'health_gain',
'health_loss',
]
class mod_handler:
pass
From the source of the damage take any modifiers that are of type damage and apply to source, from the recipient of the damage take any modifiers that are of type damage and apply to recipient
Mods may be OWNED by an entity but APPLY to another entity
Apply as bystander
Mods could be linked to an entity or linked to an entity type / group
I want there to be 50% increase to healing
And prevent all healing
And I want them to interact
The player would have the 50% increase and the enemy would have the prevent
But the enemy would not be a source or recipient in the exchange, hence the issue
Apply to, check type, applies to owner
Fire Sale
- Cards in hand cost one less mana to play
This is a modifier owned by the card, it applies to the owner of the card (the player), and therefore to all other cards in the player's hand
Card cost therefore checks the player's valid mana modifiers
And would have to also check enemy mana modifiers that apply to the player
There needs to be a concept of applies to:
Self
All but self
All
Certain types of entity
Suppose Fire Sale only affected Attack cards, how would that work
The type would be card
- The subtype would be attack
- The attribute would be mana
- The reduction would be 1
Check .class
Cards would be of .class = 'card
Cards could then have type = 'damage'
A card checking its mana would know its owner, and all other cards in hand
It would check all relics and traits of the owner
It would check all modifiers from cards in hand (or otherwise)
It would check all modifiers from enemies that are imposed on the player
def get_modifiers(card):
'''Get all modifiers that apply to a card'''
owner = card.owner
modifiers = []
for relic in owner.relics:
if card.class in relic.valid_classes:
if card.type in relic.valid_types:
modifiers.append(relic.modifier)
for trait in owner.traits:
if card.class in trait.valid_classes:
if card.type in trait.valid_types:
modifiers.append(trait.modifier)
for item in player.hand:
for trait in item.traits:
if card.class in trait.valid_classes:
if card.type in trait.valid_types:
modifiers.append(trait.modifier)
"""
^$SECTION
Each modifier is an object with a method and priority
Possibly have derivative modifiers that only affect other modifiers, such as for a card that applies strength 5 times, the modifier would essentially tie itself to that modifier, or for a modifier that cancels all multiplicative modifiers it would stop them applying
Find all contextual modifiers
Filter any modifiers that do not meet their internal conditions
Find any modifiers that care about those modifiers
1
# Alter and filter these modifiers by these external conditions
””” All Relics should have an attribute for active - For the main purpose being counting relics that only do something on a certain count and never again that battle - They should have Truth based attributes for the contexts in which they are active so that they are ignored in general
”””
”””
Next turn do X affects: - Gain the next turn do this affect and at the start of next turn resolve it
Barricade - Block is not removed at the start of turn.
For the next x turns block is not removed at the start of your turn - Would fully avoid the block removal phase
Callipers - Only 15 block is lost at the start of each turn - Would activate when block is actually being removed
”””
Other Names: Powers Talents Artifacts Capacities Enchantments Aspects Features Attributes Faculties Potencies
Requests should be objects with value and context etc.
Vulnerable - card, damage, incoming, multiply Divinity - card, damage, outgoing, multiply Intangible - all, damage, incoming, drop Boot - card, damage, outgoing, lift Magic Flower - all, health_gain, incoming, multiply
> Possibly add an attribute for self.applies_to_modifier - to mean that it affects a modifier directly not a value
self.variant = ‘mod’ self.variant = ‘admod’
admods override mods
Instead of card having its own strength modifier Perhaps it has its own interaction with a strength modifier Such that if it wants to apply strength (or vigor) 5 times, it can
Cards could have AdMods which alter modifiers
Cards have components - They enact components
Components have effects - And bestow effects
Effects may have modifiers
Ex: - Iron Wave - Component: Damage(self, ‘damage’) - Effect/Mod = Strength(player, ‘strength’) - Component: Block(self, ‘block’)
1
2
3
- Heavy Blade
- Component: HeavyDamage(self, 'damage')
- Effect/Mod = HeavyStrength(player, 'strength')
Possibly allow for two of the same Modifier “type” like strength modifiers, but only have one be active
Player could have strength effect, with value, and apply as needed, meaning effects = player.effects + player.strength + player.dexterity
stackable non-stackable unique
1
2
3
4
5
6
7
@property
def count(self):
return self._count
@count.setter
def count(self, value):
self._count = value
Intensity, Perpetual - Increase value, persists forever Intensity, Count - Increase value, persists until counter reaches 0
Count, Duration - Count per turn, number of turns Intensity, Count, Duration - Your next 3 attacks deal 8 extra damage this turn
Strength (x): Info: Your attacks deal (x) extra damage Duration: Perpetual Value: Increase (x)
Echo Form (x): Info: Your first (x) cards each turn play twice Duration: Every turn Count: Decrease on card play Reset on turn start Value: Increase (x)
Vigor (x): Info: Your next attack deals (x) extra damage Duration: Perpetual Count: Decrease on next attack play Value: Increase (x)
Double Tap: Info: Your next (x) attack cards play twice this turn Duration: One turn, remove at the end of the turn Count: Decrease on attack card play Value: Increase (x)
A card should make a request A damage request
Possible subtypes for requests such as true damage
Hypothetical Request:
Card damage of 6 from player to enemy Card has Heavy which applies strength 3 times Player has strength of 4 Player has pen nib for double damage on this attack Enemy has vulnerable for 1.5x damage
Strength is an additive modifier Pen nib is a multiplicative modifier Vulnerable is a multiplicative modifier
Card applying strength 3 times is a meta modifier that alters another modifier
Player strength applies in the context of damage from cards from player
Pen nib applies in the context of damage from cards from player
Vulnerable applies in the context of damage from cards to enemy
Heavy is a meta modifier that applies in the context of damage from cards but applies to modifier named ‘strength’
^$SECTION
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# Overview:
# Enemy has 3 thorns
# Player has default ironclad deck
# Player has 3 mana
# Enemy has curl up 8 and 17 HP, it gains 2 strength each turn
# All tooltips are visible
# HP and block are visible
# Enemy is a worm image
# Chosen has 1 HP and has text "If this enemy dies by its own hand, applies Hex to all its enemies."
# Hex adds a dazed to your draw pile whenever you play a skill
# The player has a relic to start battles with 1 strength
# NEARLY NO COMPLICATIONS ARE ADDED YET
# Possible next addition is Flex card and its buff / debuff
# Relic whenever you gain strength gain 1 more, meaning flex now scales
import pygame
# ~ New Class
class MultiLayeredDrawQueue:
def __init__(self):
"""Initialise a draw queue"""
self.items = []
def add(self, surface, rectangle, layer = 1):
"""Add surface, rectangle and layer to draw queue as a single item"""
item = (surface, rectangle, layer)
self.items.append(item)
def draw(self, display, clearing = True):
"""Draw all items from the queue to the display, sorted by layer"""
layer = lambda item: item[2]
self.items.sort(key = layer)
for item in self.items:
surface, rectangle, _ = item
display.blit(surface, rectangle)
if clearing:
self.items.clear()
# ~ Entity Methods
class EntityMethods:
def apply_damage(self, value):
"""Calculate damage and apply subtypes of damage"""
block_start = self.block
block_final = max(0, block_start - value)
block_loss = block_start - block_final
hp_loss = max(0, value - block_start)
if block_loss:
self.block_loss(block_loss)
if hp_loss:
self.hp_loss(hp_loss)
def apply_block_shed(self, value):
"""Apply a given block shed value"""
assert value >= 0, 'Block shed cannot be negative'
self.block -= value
def apply_block_loss(self, value):
"""Apply a given block loss value"""
assert value >= 0, 'Block loss cannot be negative'
self.block -= value
def apply_block_gain(self, value):
"""Apply a given block gain value"""
assert value >= 0, 'Block gain cannot be negative'
self.block += value
def apply_hp_loss(self, value):
"""Apply a given hp loss value"""
assert value >= 0, 'HP loss cannot be negative'
self.hp -= value
def apply_hp_gain(self, value):
"""Apply a given hp gain value"""
assert value >= 0, 'HP gain cannot be negative'
self.hp += value
def apply_mana_loss(self, value):
"""Apply a given mana loss value"""
assert value >= 0, 'Mana loss cannot be negative'
self.mana -= value
def apply_mana_gain(self, value):
"""Apply a given mana gain value"""
assert value >= 0, 'Mana gain cannot be negative'
self.mana += value
def apply_effect_gain(self, value, effect):
"""Apply a given effect"""
assert value >= 0, 'Effect gain value cannot be negative'
^$SECTION
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
--- Package Structure
parent_directory/
│
├── package_name/
│ │
│ ├── __init__.py
│ ├── locals.py
│ ├── constants.py
│ ├── functions.py
│ ├── classes.py
│
├── __init__.py
├── setup.py
├── readme.txt
> ├── install.py > My custom script to install the package without the use of command line
--- How to Install Python Packages via Command Line
< Change directory to the directory where setup.py resides
< cd parent_folder
< Install the package to Python PATH
< pip install .
< Or install the package with a given python version
< python -m pip install . > python -m tells the interpreter to run the script as a module, and specifies a python version
--- How to Install Python Packages via Custom Script
< Run setup.py in the parent_directory
< Answer prompts on whether or not you want the package to be editable / in development mode
--- Installing Python Packages in Editable / Development Mode
< To install in editable mode add the editable tag "-e"
< pip install -e .
< This creates a link to the original files allowing you to change them and see the results
< This will create a file in \AppData\Local\Programs\Python\Python310\Lib\site-packages:
> package_name.egg-link
< This egg-link file will tell your computer where to find the package files
< This allows you to edit them while still allowing scripts to access them
* Once done it is important to install the package without the editable tag
--- Foldered Package Structure
parent_directory/
│
├── package_name/
│ │
│ ├── folder_one/
│ │ │
<│ │ ├── __init__.py < init in folder_one
│ │ ├── locals.py
│ │ ├── constants.py
│ │
│ ├── folder_two/
│ │ │
<│ │ ├── __init__.py < init in folder_two
│ │ ├── functions.py
│ │ ├── classes.py
│ │
<│ ├── __init__.py < init in package_directory
│
├── setup.py
├── readme.txt
├── install.py
< Default Layout for setup.py
from setuptools import setup
setup(name = 'package_name',
version = '0.1',
description = 'Testing installation of package called package_name.',
url = '#',
author = 'auth',
author_email = 'author@email.com',
license = 'MIT',
packages = ['package_name'],
zip_safe = False)
< Usage of install.py
< Runs via VSCode F5 to Run Python File
< Prompts the user if they want to install in editable mode or normal mode
--- Fixing __init__.py in Foldered Packages
foldered_package_template/
│
├── foldered/
│ │
│ ├── core/
│ │ │
<│ │ ├── __init__.py #B1
│ │ ├── initialisation.py
│ │
│ ├── draw/
│ │ │
<│ │ ├── __init__.py #B2
│ │ ├── rectangle.py
│ │
│ ├── experimental/
│ │ │
<│ │ ├── __init__.py < #B3
│ │ ├── experimental.py
│ │
│ ├── maths/
│ │ │
<│ │ ├── __init__.py < #B4
│ │ ├── angles.py
│ │ ├── lines.py
│ │ ├── points.py
│ │
│ ├── sprite/
│ │ │
<│ │ ├── __init__.py < #B5
│ │ ├── enemy.py
│ │ ├── entity.py
│ │ ├── player.py
│ │ ├── sprite.py
│ │
>│ ├── __init__.py < #A1
│
├── setup.py
├── readme.txt
├── install.py
> __init__ A1 should read
from . import draw, core, experimental, maths, sprite
< __init__ B1 should read
from . import initialisation
< __init__ B2 should read
from . import rectangle
< __init__ B3 should read
from . import experimental
< __init__ B4 should read
from . import angles, lines, points
< __init__ B5 should read
from . import sprite, entity, player, enemy
< You need to directly import folders to each outermost __init__.py
< This is done with:
> from . import folder_name
< You need to directly import modules to each innermose __init__.py
< This is done with:
> from . import module_name
^$SECTION
1
^$SECTION
1
^$SECTION
1