Archive update v0.53.2

This commit is contained in:
Eliot Partridge
2019-09-17 20:19:23 -05:00
parent ac640dbe1a
commit 2c4e29eb70
133 changed files with 8047 additions and 9286 deletions
+6
View File
@@ -0,0 +1,6 @@
/config.json
/build
/build-copy
/node_modules
/.vs
+2 -529
View File
@@ -1,529 +1,2 @@
#### v0.53.1
- Switched to autumn season
- Updated custom servers (about page)
- Added option to import and export settings and actions on action bar
- Added different colors of cushions for house customization
- Fixed scroll wheel not working on Firefox
- Fixed not being able to place items behind interactive items like boxes or barrels
- Fixed issue with crystal light
#### v0.53.0
- Added custom servers (about page)
- Added export option for ponies (character page)
- Added editing house floor tiles
- Added more placeable object for the house
- Added way to cycle backwards through item list by using <kbd>Shift</kbd> + <kbd>E</kbd> while holding the hammer tool
- Added <code>/lockhouse</code> and <code>/unlockhouse</code> commands for preventing other people from modifying the house
- Added <code>/removetoolbox</code> and <code>/restoretoolbox</code> commands for removing and restoring toolbox object from house map
- Added switching placeable items and tiles by using mouse wheel
- Added switching tools using <kbd>Shift</kbd> + scroll wheel when you're inside the house
- Unlocked other objects in the house for removing
- Unlocked walls in front of windows from being placed
- Increased house object limit
- Fixed issues with house map saving using <code>/savehouse</code> command
- Fixed being able to place objects in the house behind wall
#### v0.52.0
- Added cave/mines map
- Added 2 new cave/mine sound tracks
- Added new cave/mine objects and decorations
- Added bunnies around the map
- Added direction signs
- Added pickable ropes in mines
- Added building and removing walls in party house using saw tool
- Added removing furniture in party house using broom tool
- Added placing furniture in party house using hammer tool
- Added "Switch tool" and "Switch item to place" actions
- Added carrot field in top right corner of the map
- Added more fruits to fruit box
- Added sort tags to character descriptions (the same names are sorted by description)
- Added character description help tooltip
- Added some new emotes
- Improved sorting of character list
- Improved tags dropdown
#### v0.51.2
- Added character descriptions and tags
- Added song switching when changing maps
- Fixed performance issues with login server
- Fixed issues with rendering avatars on friend list
- Fixed issues with displaying load error messages
- Improved character searching on character list
- Improved map compression
#### v0.51.1
- Fixed multiple issues with party maps
- Fixed being able to permanently hide your own character
#### v0.51.0
- Added party house on party island
- Added 2 new house sound tracks
- Added new objects for party house
- Added magic action and <code>/magic</code> command (usable by unicorns)
- Added "darken locked outlines" option to character editor
- Added permanent hides
- Added list of permanent hides to account settings
- Changed ignore party invites to not apply to friends
- Updated look of cliffs
- Fixed socks resetting if only back leg socks are set
- Fixed issues with invisible or duplicate player character
- Fixed issues with blurring of action buttons when action bar is scrolled
- Fixed issues with collision on boat bridge
- Fixed some cyrillic characters not working for expressions correctly
- Fixed rendering issues in character editor
- Fixed issues with water animation when holding lantern
- Fixed some rock animations overlapping with terrain
- Fixed apples spawning on water
#### v0.50.0
- Combined portuguese and spanish safe servers into one
- Added new 18+ russian server
- Added swimming
- Added new sock patterns
- Added setting separate accessory for each leg
- Added switch for disabling outline darkening
- Added customizable eyelash color
- Added character count to character selection list
- Added pickable flower on party island
- Added rocks in water
- Added benches
- Added smaller island bit
- Added box of fruits on party island
- Added rare pickable pear and banana
- Increased party size to 30
- Increased friend limit to 100
- Fixed not updating last used pony when using swap pony option
- Fixed game update issues after server restart
#### v0.49.0
- Added new 18+ non-ERP server
- Added new Safe Spanish server
- Added new Safe Portuguese server
- Added character swapping
- Added online/offline status switch to account dropdown
- Added scrolling of action bar on PC using mouse wheel
- Added setting separate eye white color for each eye
- Increased base character limit to 200 and supporter character limits to +200/+300/+450
- Fixed issues with game joining when spawning at the edge of the map
- Fixed gamepad X button causing pony to sit down
- Fixed player hiding getting reset on server restart
- Fixed connection issues in some cases
- Improved game performance
#### v0.48.2
- Added flying over cliffs, fences and some other objects
- Increased hide limit to 1000
- Fixed issues with collision detection
- Fixed issues with switching maps
- Fixed issues with grass on party island map
#### v0.48.1
- Added toy stash and item stash to party island
- Increased party size to 20
- Fixed issues with server restart
- Fixed issues with issues with party island map in some cases
#### v0.48.0
- Added harbor
- Added party island map
- Added party island music tracks
- Added <b>bright night</b> settings option, that makes game brighter during in-game night
- Added past supporter status
- Added extra pony slots for past supporters
- Fixed issues with collision inconsistencies
- Fixed friend tag and name color showing even when switched to "show as offline"
- Fixed commands not working in whispers
- Fixed camera issue after joining to the game
- Fixed <kbd>-</kbd> and <kbd>=</kbd> shortcuts not working on Firefox
- Fixed issues when selecting player from chatlog
- Fixed shortcut for hiding public chat
- Improved displaying party and whisper commands in chatlog
- Improved collision handling
- Improved game performance
- Increased friend limit
- Updated swear filters
#### v0.47.0
- Added friend list
- Added whispers (private messages)
- Added <code>/w &lt;name&gt;</code> and <code>/whisper &lt;name&gt;</code> command
- Added <code>/r</code> and <code>/reply</code> command
- Added "Ignore friend requests" settings option
- Added "Only allow whispers from friends" settings option
- Fixed issues with duplicate characters in certain cases
- Fixed issues with game not rendering after switching apps or tabs in some cases
- Fixed game loading performance issues
- Fixed account name not saving properly when entering name of maximum allowed length
- Improved performance
- Improved networking
- Improved server performance
- Updated swear filters
#### v0.46.6
- Reverted performance improvement leading to issues with sprite rendering on some browsers
#### v0.46.5
- Moved rose sign
- Fixed some commands not working properly when using uppercase letters
- Fixed issues with map regions not loading correctly in certain cases
- Fixed issues with map not loading in certain cases
- Fixed server-side performance issues
- Fixed issues with AFK check
- Fixed issues with colors missing on held items in certain cases
- Updated swear filters
#### v0.46.4
- Fixed issues with incorrect color for chat type indicator in certain cases
- Fixed issues with joining after disconnect resulting in missing or duplicated characters
- Fixed issues with error reporting
- Improved some settings labels
- Improved server performance
#### v0.46.3
- Changed longer hide to 10 days instead of 7
- Increased hide limit to 25
- Updated swear filters
- Fixed critical server-side issues
#### v0.46.2
- Fixed items spawning on cliffs
- Fixed issues with date of birth month list
- Updated swear filters
#### v0.46.1
- Fixed issues with collisions
- Fixed issues with disappearing player character in certain cases
- Fixed items spawning on cliffs
- Fixed some misplaced objects
- Fixed some issues with object sorting
#### v0.46.0
- Increased map size
- Added new locations to the map
- Added new plants
- Increased party size limit to 10 players
- Increased hide limit to 20
- Fixed multiple issues with party UI
- Fixed issues with music playback
- Fixed issues with disappearing player character in certain cases
- Fixed missing range limitation for changing tiles
- Improved server performance
- Improved rendering reliability and performance
- Updated swear filters
- Updated server infrastructure
#### v0.45.3
- Fixed issues with saving account settings in some cases
- Fixed server performance issues
- Fixed some styling issues
- Updated swear filters
- Improved graphics performance
- Improved server logging and performance analysis tools
#### v0.45.2
- Added 25 more pony slots for all players
- Removed collectable eggs
- Fixed graphics memory leak issue
- Fixed graphics initialization issues in certain cases
- Fixed performance issues when joining to the game
- Improved networking performance
#### v0.45.1
- Added egg donation barrel
- Fixed issues when saving settings in certain cases
- Fixed issues with action bar on desktop PCs with touchscreens
- Fixed disconnect issues in some cases
- Fixed broken link in about page
#### v0.45.0
- Added collectable eggs
- Added egg emoji
- Added <code>/eggs</code> command
- Added eggs command action
- Added keyboard shortcut <kbd>F3</kbd> for toggling public chat
- Fixed custom filters not working for player names
- Fixed styling issues on Yandex browser
- Fixed tongue not visible on expression action buttons
- Reduced memory usage
- Improved graphics resource allocation management
- Improved memory usage for graphics resources
- Improved chatlog performance
#### v0.44.2
- Fixed actions modal styling issues on Internet explorer
- Fixed error when running out of memory in some cases
- Fixed not being able to join server in some cases
#### v0.44.1
- Changed command actions to use last used chat type (party/public)
- Fixed actions modal scrolling by itself on mobile
- Fixed some closed eyes not counted correctly as closed for sleeping emotes
#### v0.44.0
- Added action bar
- Added action bar customization via "Actions" option in in-game settings dropdown
- Fixed issues with showing incorrect party state after disconnect in certain cases
#### v0.43.2
- Added filters section to in-game settings
- Added custom chat filter to in-game settings
- Improved rendering performance
- Fixed chatlog range indicator showing incorrectly in some cases
- Fixed incorrect errors when failed to initialize graphics device
- Fixed connection issues when cancelling joining to the game
- Updated swear filters
#### v0.43.1
- Added ignore public chat option to in-game settings
- Added button shortcut for opening chat on controller
- Added date of birth field to account settings
- Changed eyes when using uppercase Q letters in expressions <code>QcQ</code>
- Changed character name validation
- Improved camera shifting when opening mobile keyboard
- Improved some letters in pixel font
- Fixed issues when mobile keyboard is opened
- Fixed incorrect error messages for party invites in some cases
- Updated swear filters
#### v0.43.0
- Added new manes
- Added chatlog range setting
- Added fps counter (option in settings)
- Changed "see through objects" and "chatlog opacity" settings to be saved on server
- Changed night to be lighter on low graphics settings to compensate for missing lights
- Improved party invite error messages
- Updated map with new flower and clover patches
- Updated swear filters
- Removed some expression combinations from PG rated servers
- Fixed command messages in party chat being colored white instead of yellow
- Fixed issues with part of game screen being outside the screen bounds on mobile devices in some cases
- Fixed issues with pixel scaling on low graphics mode
- Fixed issues with game screen turning green or black
- Fixed issues with game loading
- Fixed issues with music playing before the game loads
- Fixed issue with reloading game in some cases
- Fixed styling issues
#### v0.42.4
- Changed season to spring
- Updated swear filters
- Fixed some issues with account alerts
#### v0.42.3
- Added <code>:thinking:</code> emoji
- Added account alerts for notifying players of account related issues
- Removed some expression combinations from PG rated servers
- Updated swear filters
- Fixed graphical glitch with some expressions when using beak muzzle option
- Fixed sign-in error showing off-screen on some devices
- Fixed some disconnect issues
- Improved performance
#### v0.42.2
- Fixed issue with game breaking in some cases when the connection is interrupted
#### v0.42.1
- Fixed issues with color pickers on some mobile devices
#### v0.42.0
- Added rose sign
- Added <code>/love</code>, <code>/<3</code> command
- Updated common issues help section
- Updated in-game settings window
- Fixed some issues with game screen positioning on mobile devices
- Fixed some issues with chatlog scrolling
- Fixed issues with switching chat mode
#### v0.41.0
- Added new facial hair styles and patterns
- Added cookie stand
- Fixed issue with chatlog tab switching on Firefox
- Fixed issue with bats getting stuck when returning to trees
- Fixed issue with characters with no wings appear to hover above ground in certain cases
- Fixed issue with delay when joining to the game
- Fixed errors when using remote buttons while playing the game
- Fixed some off-screen messages appearing in chatlog
- Fixed some styling issues
#### v0.40.1
- Remove loading performance optimization due to Yandex browser bug
#### v0.40.0
- Added chatlog
- Added chatlog settings
- Added settings for walking by default
- Fixed graphical glitch when booping while sitting
- Fixed issues with critter movement
- Fixed being able to be hidden by yourself in certain situations
- Fixed incorrect error message displayed in certain situations
- Improved game loading performance
#### v0.39.4
- Added number in queue indicator when joining to the game takes longer than a second
- Fixed issue with game not loading when graphics initialization takes longer than connecting to the server
- Fixed issues with gamepad dpad moving the character
- Fixed issues with iPad keyboards where each key would only work once, the keys will still get stuck occasionally due to iOS bug, we recommend to use arrow keys for movement instead of WASD keys.
#### v0.39.3
- Fixed issues with sending long messages on Edge and Internet Explorer browsers
#### v0.39.2
- Removed Xmas decorations
- Changed support contact email
- Fixed some issues with disconnects
- Fixed party chat messages visible when party member is on another map
- Improved networking performance
- Improved server performance
#### v0.39.1
- Added gift donation spot where you can donate gifts and reduce your gift count
- Added additional restart buttons to avoid getting stuck in game updating state
- Added remembering of <code>/s1</code>, <code>/s2</code> and <code>/s3</code> chat mode for next message
- Added more logging for analyzing sign-in issues
- Fixed issues with mobile keyboard when the game is installed to home screen
- Fixed issues with game updating
- Fixed issues with missing files during game update process
- Fixed focusing pony name input field on mobile devices causing mobile keyboard to open
- Fixed being able to see lantern lights of lanterns held by hidden players in some cases
#### v0.39.0
- Added xmas decorations
- Added gifts with collectable toys (use <kbd>E</kbd> or <code>/open</code> command or <em>gift button</em> on mobile to open the gift)
- Added <code>/droptoy</code> command for getting rid of toy on your head
- Added <code>/toys</code> for listing amount of collected toys
- Added candy/cookie stands
- Added picking up apples and oranges
- Added set of most common Chinese characters
- Added Japanese Kanji characters
- Added cookie emoji
- Changed plushie sign to toy stash (keeps all your collected toys)
- Changed unicode symbol assigned for some emoji characters
- Fixed some issues with updating the game
- Fixed some issues with height difference when standing on ice
- Improved rendering performance
#### v0.38.0
- Added winter environment details
- Changed season to winter
- Fixed some performance issues
#### v0.37.3
- Fixed issues with character selection list on mobile devices
- Fixed issues with keyboard on mobile devices
#### v0.37.2
- Fixed issues with character selection
- Fixed issues when using mouse with more than 3 buttons
#### v0.37.1
- Added <code>/leave</code> command
- Removed Halloween decorations
- Fixed some duplicate objects on the map
- Fixed issues with cloudflare verification window not working properly
- Fixed issues with some sprites
- Fixed issues with opening mobile keyboard on some devices
- Fixed issues with drawing lights in certain cases
- Improved networking performance
- Improved server performance
#### v0.37.0
- Added 2 new music tracks
- Added new head, face, neck, chest and back accessories
- Added new sleeve options for chest accessories
- Added new manes
- Added new tails
- Added new face markings
- Added new horn and horn patterns
- Added new eyelashes
- Added new ears
- Added new hooves
- Added ear patterns
- Added pants back accessory
- Added Halloween decorations
- Added spooks
- Changed google sign-in icon
- Updated team and contributors lists
- Updated head accessory positions for each mane
- Fixed issues with install button
- Fixed issues with mobile keyboard in app mode
- Fixed issues with map loading in certain cases
- Fixed some sprite layering issues
- Fixed some site links not working in certain cases
- Fixed cutting off some pixels when exporting fly animation as PNG
- Fixed color in butt mark editor changing when switching tabs
- Improved rendering performance
#### v0.36.1
- Updated facebook sign-in
- Fixed some lighting issues
- Improved lighting performance
#### v0.36.0
- Changed season to autumn
- Increased map size
- Added new locations and road system
- Added some rule explanations to <em>Common issues</em> section on <em>Help</em> page
- Improved lighting engine
- Fixed issues with character not shown as flying in some cases
- Fixed issues with updating Patreon data
- Fixed issues with game update process
- Fixed issues with loosing item counters when merging accounts in certain situations
- Fixed issues with getting kicked out of the party in certain situations
- Fixed incorrect symbol used for <code>:trans:</code> emoji
#### v0.35.7
- Added warning message for some outdated browsers
- Improved networking performance
- Fixed "Um." and "Um-" text causing expression to change
- Fixed some issues with character selection
- Fixed some issues with name display
- Fixed issues with Cloudflare reloading
#### v0.35.6
- Improved server performance
- Improved network usage
#### v0.35.5
- Added option to install the game on android devices
- Improved startup performance
- Improved server performance
- Fixed party list covering chat buttons in some cases on small screens
- Fixed issues with experimental progressive map loading
#### v0.35.4
- Added experimental progressive map loading
- Added power saving option to settings
- Improved performance of map loading
- Reworked internal server communication
- Fixed layering issues with some accessories
- Fixed incorrect hitbox for flying ponies
- Fixed issues with drawing ponies at the edge of the screen
- Fixed some unhelpful error messages
- Fixed some words interpreted as expressions
- Fixed some issues with movement on slow networks
- Fixed some issues with boop animation when lying down
- Fixed some issues with error reporting
#### v0.35.3
- Added <code>b.d</code> / <code>6.6</code> expression
- Added fly animation to character editor
- Improved visibility checking
- Improved networking performance
- Fixed issue with disappearing party member portraits
- Fixed server-side performance issues
- Fixed update notification not showing when joined during an update
- Fixed server shutdown issues
- Fixed incorrect hitbox for flying characters
- Fixed server-side timing issues
#### v0.35.2
- Fixed performance issues when a lot of users are joining the server at the same time
#### v0.35.1
- Fixed performance issues with ignore system and hiding system
- Fixed performance issues when a lot of users are joining the server at the same time
#### v0.35.0
- Added flying - <kbd>C</kbd> key or <kbd class="gamepad gamepad-dpad"><div class="dpad-up"></div></kbd> button or <code>/fly</code> command
- Added booping while flying
- Added flying above water
#### v0.0.1
- Initial release
+9 -4
View File
@@ -1,13 +1,13 @@
creator(s):
Shino (https://www.deviantart.com/shinodage)
ChiraChan (https://www.deviantart.com/chiramii-chan, https://chirachan-art.tumblr.com/)
Velenor (https://www.deviantart.com/velenor)
Velenor
ShareMyShipment (https://www.deviantart.com/sharemyshipment)
Paulpeoples (https://www.deviantart.com/paulpeopless)
Meno (https://www.deviantart.com/menojar)
Meno (https://twitter.com/menojar, https://www.deviantart.com/menojar)
Goodly (https://www.deviantart.com/goodlyay)
TioRafaJP (https://www.deviantart.com/tiorafajp, https://www.youtube.com/user/RafaelJP2)
CyberPon3 (https://www.deviantart.com/cyberpon3)
CyberPon3 (https://twitter.com/CyberPon3)
OtakuAP (https://www.deviantart.com/otakuap)
Disastral
Velvet-Frost (https://www.deviantart.com/velvet-frost)
@@ -25,13 +25,18 @@ creator(s):
Towmacow Waffles (https://www.deviantart.com/towmacowwaffles)
OrchidPony (https://www.deviantart.com/orchidpony)
Cherry Cerise (https://www.deviantart.com/cherryceriseart)
Radio
RADIOstations (https://twitter.com/stagbeeble)
Ultimate Fluff
SC (https://0somecunt0.tumblr.com/tagged/sfw)
SailorDolpin (https://vk.com/id324582699)
Deeraw (https://www.deviantart.com/deerdraw, https://twitter.com/TheOnlyDeeraw)
Pony Town's art assets are released under the CC BY-NC 4.0 non-commercial license.
The assets cannot be used for commercial purposes in any way including crowdfunding such as Patreon or PayPal donations without permission.
To discuss licensing, contact [email protected].
Creative Commons Attribution-NonCommercial 4.0 International Public
License
+5
View File
@@ -2,6 +2,11 @@ creator(s):
Wandering Artist (https://wanderingartist.bandcamp.com/, https://www.youtube.com/user/WanderingArtistMusic)
Pony Town's music assets are released under the CC BY-NC 4.0 non-commercial license.
The assets cannot be used for commercial purposes in any way including crowdfunding such as Patreon or PayPal donations without permission.
To discuss licensing, contact [email protected].
Creative Commons Attribution-NonCommercial 4.0 International Public
License
+80 -25
View File
@@ -1,6 +1,13 @@
# Pony Town
# Pony Town Custom Server
A game of ponies building a town
Setup instructions
## Licensing
Pony Town's code is released to public domain. The art and music assets are released under the CC BY-NC 4.0 non-commercial license.
The assets cannot be used for commercial purposes in any way including crowdfunding such as Patreon or PayPal donations without permission.
See `LICENSE_CODE.txt`, `LICENSE_ART.txt` and `LICENSE_MUSIC.txt` files for definitions.
To discuss licensing, contact `[email protected]`.
## Prerequisites
@@ -36,7 +43,7 @@ npm install
## Setting up OAuth keys
Get OAuth keys for authentication platform of your choice (github, google, twitter, facebook, vkontakte, patreon)
Get OAuth keys for authentication platform of your choice (github, google, twitter, facebook, vkontakte)
### Github
@@ -125,8 +132,9 @@ Add `config.json` file in root directory with following content. You can use `co
"host": "http://localhost:8090/",
"local": "localhost:8090",
"adminLocal": "localhost:8091",
"secret": "<some_random_string_here>",
"token": "<some_random_string_here>",
"proxy": false, // set to true or to the ip of your proxy server if hosting pony town behind a proxy like nginx
"secret": "<some_random_string_here>", // use a long and random string here, it's crucial for your security that nobody else knows this value
"token": "<some_other_random_string_here>", // use a long and random string here, it's crucial for your security that nobody else knows this value
"db": "mongodb://<username>:<password>@localhost:27017/<database_name>", // use values you used when setting up database
"analytics": { // optional google analytics
"trackingID": "<tracking_id>"
@@ -161,30 +169,23 @@ Add `config.json` file in root directory with following content. You can use `co
## Running
Production environment
For development, see "Running in development" section.
### Production environment
```bash
npm run build
npm start
```
Adding/removing roles
### Beta environment (with dev tools and in-development features)
```bash
node cli.js --addrole <account_id> <role> # roles: superadmin, admin, mod, dev
node cli.js --removerole <account_id> <role>
npm run build-beta
node pony-town.js --login --admin --game --tools --beta
```
To setup superadmin role use following command
```bash
node cli.js --addrole <your_account_id> superadmin
```
Admin panel is accessible at `<base_url>/admin/` (requires admin or superadmin role to access)
Tools are accessible at `<base_url>/tools/` (only available in dev mode or when started with --tools flag)
Starting as multiple processes
### Starting as multiple processes
```bash
node pony-town.js --login # login server
@@ -201,14 +202,18 @@ It is recommended to run processes with larger memory pool for large user bases
node --max_old_space_size=8192 pony-town.js --game main
```
Beta environment (with dev tools and in-development features)
### Recommended production server update procedure
```bash
npm run build-beta
node pony-town.js --login --admin --game --tools --beta
```
1. Build everything
2. Issue a restart notice (from admin panel `<base_url>/admin/`)
3. Wait around 30s
4. Issue shutdown servers command (from admin panel `<base_url>/admin/`)
5. Wait for the user count to become 0
6. Restart the server
Running in development
You do not need to shut down the server to build.
### Running in development (compiles faster and has extra tools)
```bash
npm run ts-watch # terminal 1
@@ -223,9 +228,28 @@ gulp dev --test # run with tests
gulp dev --coverage # run with tests and code coverage
```
### Adding/removing roles
```bash
node cli.js --addrole <account_id> <role> # roles: superadmin, admin, mod, dev
node cli.js --removerole <account_id> <role>
```
To setup superadmin role use following command
```bash
node cli.js --addrole <your_account_id> superadmin
```
### Additional tools
Admin panel is accessible at `<base_url>/admin/` (requires admin or superadmin role to access)
Tools are accessible at `<base_url>/tools/` (only available in dev mode or when started with --tools flag)
## Customization
- `package.json` - settings for title and description of the website
- `changelog.md` - your public updates
- `assets/images` - logos and team avatars
- `public/images` - additional logos
- `public` - privacy policy and terms of service
@@ -239,5 +263,36 @@ gulp dev --coverage # run with tests and code coverage
### Custom map introduction
- `src/ts/common/entities.ts` - adding entities
- `src/ts/server/start.ts:35` - adding custom map to the world
- `src/ts/server/map/customMap.ts` - commented introduction to customizing maps
### Adding assets
1. Edit sprites (`assets-source` folder)
2. Compile sprites (`gulp dev --sprites`)
3. Add relevant code
4. (In some cases) compile sprites again
## Changelog
### Pony.Town v0.53.2
- Added changes from Pony.Town update v0.53.2 (https://pony.town/about)
- Updated readme with further instructions on updating server and adding assets
- Updated pony compression to handle up to 63 items per slot instead of 31
- Added Visit PT button
- Added disclaimer notice to Home and About
- Updated licenses
- Removed some unused assets
- Updated Contributor list
- Removed "Pony Town" from public pages
### Pony.Town v0.53.1
- Added option to import and export settings and actions on action bar
- Added different colors of cushions for house customization
- Fixed scroll wheel not working on Firefox
- Fixed not being able to place items behind interactive items like boxes or barrels
- Fixed issue with crystal light
### Initial release - Pony.Town v0.53.0
- Added custom servers
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
+3 -2
View File
@@ -7,8 +7,9 @@
"host": "http://localhost:8090/",
"local": "localhost:8090",
"adminLocal": "localhost:8091",
"secret": "gfhfdshtrdhgedryhe4t3y5uwjthr",
"token": "sdlfgihsdor8ghor8dgdrgdegrdg",
"proxy": false,
"secret": "<some_random_string_here>",
"token": "<some_other_random_string_here>",
"db": "mongodb://<username>:<password>@localhost:27017/<database_name>",
"oauth": {
"google": {
+4
View File
@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore
+4423 -4541
View File
File diff suppressed because it is too large Load Diff
+6 -9
View File
@@ -1,11 +1,12 @@
{
"name": "pony-town",
"version": "0.53.1-alpha",
"version": "0.53.2",
"description": "A game of ponies building a town",
"main": "pony-town.js",
"scripts": {
"start": "node pony-town.js --login --admin --game",
"start-inspect": "node --inspect pony-town.js --login --admin --game",
"startlocal": "node pony-town.js --login --admin --game --local",
"start-inspect": "node --inspect pony-town.js --login --admin --game --local",
"lint": "tslint -c tslint.json src/ts/**/*.ts",
"dev": "gulp dev",
"dev-sprites": "gulp dev --sprites",
@@ -24,7 +25,7 @@
"webpack-admin": "npm run webpack-prod -- --env.admin",
"webpack-stats": "npm run webpack-prod -- --env.debug --json > stats.json",
"webpack-verbose": "npm run webpack-prod -- --verbose",
"wds": "webpack-dev-server --config webpack.dev.js --progress --color --debug --port 8091",
"wds": "webpack-dev-server --config webpack.dev.js --progress --color --debug --port 8091 --local",
"build": "gulp build",
"build-beta": "npm run build -- --beta --tools",
"build-fast": "npm run build -- --parallel",
@@ -37,11 +38,6 @@
"test-ts": "node --max-old-space-size=8192 ./node_modules/mocha/bin/mocha \"src/ts/tests/**/*.spec.ts\" -r ts-node/register -R progress --exit",
"test-ts-watch": "npm run test -- --watch --watch-extensions ts",
"test-inspect": "mocha \"src/scripts/tests/**/*.spec.js\" -R progress --inspect --exit --watch",
"check-filters": "node src/scripts/experiments/checkFilters.js",
"check-names": "node src/scripts/experiments/checkNames.js",
"check-links": "node src/scripts/experiments/checkLinks.js",
"check-messages": "node src/scripts/experiments/checkValidMessages.js",
"check-cms": "node src/scripts/experiments/checkCms.js",
"update-angular": "npm update @angular/common @angular/compiler @angular/core @angular/forms @angular/platform-browser @angular/platform-browser-dynamic @angular/router @angular/animations @angular/compiler-cli @angular/platform-server @angular/cdk --save",
"latest-angular": "npm i @angular/common@latest @angular/compiler@latest @angular/core@latest @angular/forms@latest @angular/platform-browser@latest @angular/platform-browser-dynamic@latest @angular/router@latest @angular/animations@latest @angular/compiler-cli@latest @angular/platform-server@latest @angular/cdk@latest --save",
"update-fontawesome": "npm update @fortawesome/fontawesome-svg-core @fortawesome/free-brands-svg-icons @fortawesome/free-solid-svg-icons @fortawesome/angular-fontawesome --save"
@@ -99,6 +95,7 @@
"@types/sinon": "^7.0.13",
"@types/ua-parser-js": "^0.7.33",
"@types/uws": "^0.13.2",
"@types/webgl2": "0.0.5",
"@types/yargs": "^13.0.2",
"ag-psd": "^6.2.0",
"ag-sockets": "^4.7.0",
@@ -166,7 +163,6 @@
"passport-tumblr": "^0.1.2",
"passport-twitter": "^1.0.4",
"passport-vkontakte": "^0.3.2",
"patreon": "^0.4.1",
"postcss-loader": "^3.0.0",
"pug": "^2.0.4",
"pug-html-loader": "^1.1.5",
@@ -195,6 +191,7 @@
"typescript": "^3.5.3",
"ua-parser-js": "^0.7.20",
"uglifyjs-webpack-plugin": "^1.3.0",
"webgl-debug": "^2.0.1",
"webpack": "^4.39.3",
"webpack-bundle-analyzer": "^3.4.1",
"webpack-cli": "^3.3.7",
Binary file not shown.
Binary file not shown.
+2 -2
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>Pony Town (offline)</title>
<title>Your Town (offline)</title>
<style>
body {
background: #333;
@@ -31,7 +31,7 @@
<body>
<div>
<p>
Pony Town server is undergoing maintenance, please check back later.
Your Town server is undergoing maintenance, please check back later.
</p>
</div>
</body>
+4 -4
View File
@@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Pony Town | Privacy Policy</title>
<title>Your Town | Privacy Policy</title>
<style>
body {
font: normal 16px sans-serif;
@@ -24,10 +24,10 @@
<div class="content">
<h1>Privacy Policy</h1>
<p>
Last updated: April 27, 2018
Last updated: <!-- DATE -->
</p>
<p>
We operate https://pony.town/ (the "Site"). This page informs you of our policies regarding the collection,
We operate <!-- SITE URL --> (the "Site"). This page informs you of our policies regarding the collection,
use and disclosure of Personal Information we receive from users of the Site.
</p>
<p>
@@ -73,7 +73,7 @@
</p>
<h2>Changes To This Privacy Policy</h2>
<p>
This Privacy Policy is effective as of August 11, 2016 and will remain in effect except with respect to any
This Privacy Policy is effective as of <!-- DATE --> and will remain in effect except with respect to any
changes in its provisions in the future, which will be in effect immediately after being posted on this page.
</p>
<p>
+1 -1
View File
@@ -4,7 +4,7 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>Pony Town (reload)</title>
<title>Your Town (reload)</title>
<style>
body {
background: #333;
+1 -1
View File
@@ -4,7 +4,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>Pony Town (reset)</title>
<title>Your Town (reset)</title>
<style>
body {
background: #333;
+3 -3
View File
@@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Pony Town | Terms of Service</title>
<title>Your Town | Terms of Service</title>
<style>
body {
font: normal 16px sans-serif;
@@ -24,11 +24,11 @@
<div class="content">
<h1>Terms of Service ("Terms")</h1>
<p>
Last updated: August 11, 2016
Last updated: <!-- DATE -->
</p>
<p>
Please read these Terms of Service ("Terms", "Terms of Service") carefully before using the
https://pony.town/ website (the "Service") operated by us.
<!-- SITE URL --> website (the "Service") operated by us.
</p>
<p>
Your access to and use of the Service is conditioned on your acceptance of and compliance with
+4
View File
@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore
+4
View File
@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore
+2
View File
@@ -272,6 +272,8 @@ $argentina-blue: #74acdf;
}
}
// TODO: make $action-bar-space 46px on desktop
.actions-modal-opened {
$action-bar-space: 80px;
+19 -15
View File
@@ -15,6 +15,7 @@ export interface Contributors {
contributors: Contributor[];
}
// The Team
export const CREDITS: Credit[] = [
// example:
// {
@@ -27,28 +28,31 @@ export const CREDITS: Credit[] = [
export const CONTRIBUTORS: Contributors[] = [
{
group: 'Artists & Animators',
group: 'Music Composers',
contributors: [
{ name: 'Wandering Artist', links: ['https://wanderingartist.bandcamp.com/', 'https://www.youtube.com/user/WanderingArtistMusic'] },
],
},
{
group: 'Programmers',
contributors: [
{ name: 'CyberPon3', links: ['https://twitter.com/CyberPon3'] },
{ name: 'Industrialice' },
],
},
{
group: 'Artists',
contributors: [
{ name: 'Shino', links: ['https://www.deviantart.com/shinodage'] },
{ name: 'ChiraChan', links: ['https://www.deviantart.com/chiramii-chan', 'https://chirachan-art.tumblr.com/'] },
{ name: 'Goodly', links: ['https://www.deviantart.com/goodlyay'] },
{ name: 'TioRafaJP', links: ['https://www.deviantart.com/tiorafajp', 'https://www.youtube.com/user/RafaelJP2'] },
{ name: 'ShareMyShipment', links: ['https://www.deviantart.com/sharemyshipment'] },
{ name: 'Velenor', links: ['https://www.deviantart.com/velenor'] },
{ name: 'Velenor' },
{ name: 'Meno', links: ['https://twitter.com/menojar', 'https://www.deviantart.com/menojar'] },
{ name: 'OtakuAP', links: ['https://www.deviantart.com/otakuap'] },
],
},
{
group: 'Programmers & Artists',
contributors: [
{ name: 'CyberPon3', links: ['https://www.deviantart.com/cyberpon3'] },
],
},
{
group: 'Artists',
contributors: [
{ name: 'Disastral' },
{ name: 'Meno', links: ['https://www.deviantart.com/menojar'] },
{ name: 'CyberPon3', links: ['https://twitter.com/CyberPon3'] },
{ name: 'Paulpeoples', links: ['https://www.deviantart.com/paulpeopless'] },
{ name: 'Velvet-Frost', links: ['https://www.deviantart.com/velvet-frost'] },
{ name: 'Jet7Wave', links: ['https://www.deviantart.com/jetwave'] },
@@ -65,7 +69,7 @@ export const CONTRIBUTORS: Contributors[] = [
{ name: 'Towmacow Waffles', links: ['https://www.deviantart.com/towmacowwaffles'] },
{ name: 'OrchidPony', links: ['https://www.deviantart.com/orchidpony'] },
{ name: 'Cherry Cerise', links: ['https://www.deviantart.com/cherryceriseart'] },
{ name: 'Radio' },
{ name: 'RADIOstations', links: ['https://twitter.com/stagbeeble'] },
{ name: 'Ultimate Fluff' },
{ name: 'SC', links: ['https://0somecunt0.tumblr.com/tagged/sfw'] },
{ name: 'SailorDolpin', links: ['https://vk.com/id324582699'] },
+22 -6
View File
@@ -18,9 +18,14 @@ const SELECTED_ENTITY_BOUNDS = withAlphaFloat(ORANGE, 0.5);
function drawEntities(batch: PaletteSpriteBatch, entities: Entity[], camera: Camera, options: DrawOptions) {
const drawHidden = options.drawHidden;
let entitiesDrawn = 0;
let depthStep = 1.0 / entities.length;
for (let entity of entities) {
if (isBoundsVisible(camera, entity.bounds, entity.x, entity.y) && (!isHidden(entity) || drawHidden)) {
const depth = depthStep * entitiesDrawn;
entity.depth = depth;
batch.depth = depth;
for (const entity of entities) {
if ((!isHidden(entity) || drawHidden) && isBoundsVisible(camera, entity.bounds, entity.x, entity.y)) {
if (entity.type === PONY_TYPE) {
drawPonyEntity(batch, entity as Pony, options);
entitiesDrawn++;
@@ -45,38 +50,47 @@ function drawEntities(batch: PaletteSpriteBatch, entities: Entity[], camera: Cam
export function drawEntityLights(batch: SpriteBatch, entities: Entity[], camera: Camera, options: DrawOptions) {
const drawHidden = options.drawHidden;
let drawn = 0;
for (const entity of entities) {
if (DEVELOPMENT && (entity.type !== PONY_TYPE && !entity.drawLight)) {
console.error('Cannot draw entity light', entity);
}
if ((!isHidden(entity) || drawHidden) && isBoundsVisible(camera, entity.lightBounds, entity.x, entity.y)) {
if (isBoundsVisible(camera, entity.lightBounds, entity.x, entity.y) && (!isHidden(entity) || drawHidden)) {
if (entity.type === PONY_TYPE) {
drawPonyEntityLight(batch, entity as Pony, options);
} else {
entity.drawLight!(batch, options);
}
++drawn;
}
}
return drawn;
}
export function drawEntityLightSprites(batch: SpriteBatch, entities: Entity[], camera: Camera, options: DrawOptions) {
const drawHidden = options.drawHidden;
let drawn = 0;
for (const entity of entities) {
if (DEVELOPMENT && (entity.type !== PONY_TYPE && !entity.drawLightSprite)) {
console.error('Cannot draw entity light sprite', entity);
}
if ((!isHidden(entity) || drawHidden) && isBoundsVisible(camera, entity.lightSpriteBounds, entity.x, entity.y)) {
if (isBoundsVisible(camera, entity.lightSpriteBounds, entity.x, entity.y) && (!isHidden(entity) || drawHidden)) {
batch.depth = entity.depth;
if (entity.type === PONY_TYPE) {
drawPonyEntityLightSprite(batch, entity as Pony, options);
} else {
entity.drawLightSprite!(batch, options);
}
++drawn;
}
}
return drawn;
}
export function hasDrawLight(entity: Entity) {
@@ -99,10 +113,11 @@ export function hasLightSprite(entity: Entity) {
}
export function drawMap(
batch: PaletteSpriteBatch, map: WorldMap, camera: Camera, player: Pony, options: DrawOptions,
tileSets: TileSets, selectedEntities: Entity[],
batch: PaletteSpriteBatch, map: WorldMap, camera: Camera, player: Pony,
options: DrawOptions, tileSets: TileSets, selectedEntities: Entity[],
) {
TIMING && timeStart('forEachRegion');
batch.depth = 1.0;
if (BETA && options.engine === Engine.Whiteness) {
batch.drawRect(WHITE, 0, 0, toScreenX(map.width), toScreenY(map.height));
} else if (BETA && options.engine === Engine.LayeredTiles) {
@@ -118,6 +133,7 @@ export function drawMap(
TIMING && timeStart('drawEntities');
const entitiesDrawn = drawEntities(batch, map.entitiesDrawable, camera, options);
batch.depth = 1.0;
TIMING && timeEnd();
if (BETA || TOOLS) {
+211 -159
View File
@@ -5,7 +5,7 @@ import {
EntityState, Pony, Notification, TileType, Action, IServerActions, Season, WorldState, Holiday,
TileSets, ChatMessage, PartyInfo, Entity, DrawOptions, Engine, defaultDrawOptions, PonyStateFlags,
DoAction, ChatType, WorldStateFlags, DebugFlags, MessageType, AccountSettings, Matrix4,
FakeEntity, SelectFlags, WorldMap, MapType, MapFlags, EntityFlags, houseTiles, isValidTile
FakeEntity, SelectFlags, WorldMap, MapType, MapFlags, EntityFlags, houseTiles, isValidTile, GraphicsQuality
} from '../common/interfaces';
import {
clamp, lengthOfXY, setFlag, hasFlag, boundsIntersect, point, toInt, lerpColor, distanceXY
@@ -27,7 +27,7 @@ import { toggleWalls } from '../common/mixins';
import { getEntityTypeName, hammer, broom, createAnEntity, saw, placeableEntities, shovel } from '../common/entities';
import { hasExtendedInfo, setHeadAnimation, createPony } from '../common/pony';
import { PaletteManager } from '../graphics/paletteManager';
import { getRenderTargetSize, isWebGL2 } from '../graphics/webgl/webglUtils';
import { isWebGL2 } from '../graphics/webgl/webglUtils';
import { drawFullScreenMessage, drawNames, drawChat } from '../graphics/graphicsUtils';
import { Key } from './input/input';
import { loadAndInitSpriteSheets } from './spriteUtils';
@@ -62,7 +62,7 @@ import { initializeToys } from './ponyDraw';
import { ErrorReporter } from '../components/services/errorReporter';
import { mockPaletteManager } from '../common/ponyInfo';
import { timeStart, timeEnd, timingCollate, timeReset } from './timing';
import { bindFrameBuffer, unbindFrameBuffer, resizeFrameBuffer } from '../graphics/webgl/frameBuffer';
import { createFrameBuffer, bindFrameBuffer, unbindFrameBuffer, disposeFrameBuffer } from '../graphics/webgl/frameBuffer';
import { WebGL, initWebGL, disposeWebGL, initWebGLResources } from './webgl';
import { bindTexture } from '../graphics/webgl/texture2d';
import {
@@ -207,7 +207,6 @@ export class PonyTownGame implements Game {
offlinePony = createPony(0, 0, OFFLINE_PONY, mockPaletteManager.addArray(defaultPalette), mockPaletteManager);
supporterPony = createPony(0, 0, SUPPORTER_PONY, mockPaletteManager.addArray(defaultPalette), mockPaletteManager);
scale: number;
failedFBO = false;
rightOverride?: boolean;
headTurnedOverride?: boolean;
stateOverride?: EntityState;
@@ -247,6 +246,8 @@ export class PonyTownGame implements Game {
private deltaMultiplier = 1;
private lastDraw = 0;
private entitiesDrawn = 0;
private lightEntitiesDrawn = 0;
private lightSpriteEntitiesDrawn = 0;
private lastFps = performance.now();
private frames = 0;
private drawFps = 0;
@@ -273,6 +274,16 @@ export class PonyTownGame implements Game {
private errorReporter: ErrorReporter,
private zone: NgZone,
) {
if (settings.browser.lowGraphicsMode !== undefined) {
settings.browser.graphicsQuality = (settings.browser.lowGraphicsMode === true ? GraphicsQuality.Low : GraphicsQuality.High);
settings.browser.lowGraphicsMode = undefined;
settings.saveBrowserSettings();
}
if (settings.browser.graphicsQuality === undefined) {
settings.browser.graphicsQuality = GraphicsQuality.High;
settings.saveBrowserSettings();
}
this.scale = this.getScale();
this.audio.initTracks(this.season, this.holiday, this.map.type);
this.audio.setVolume(this.volume);
@@ -289,7 +300,22 @@ export class PonyTownGame implements Game {
return this.settings.browser.volume || 0;
}
get disableLighting() {
return !!this.settings.browser.lowGraphicsMode || this.failedFBO;
if (this.settings.browser.graphicsQuality === GraphicsQuality.Low) {
return true;
}
if (this.webgl) {
return this.webgl.failedFBO;
}
return false;
}
get disableLightMasksFixing() {
if (this.settings.browser.graphicsQuality !== GraphicsQuality.High) {
return true;
}
if (this.webgl) {
return this.webgl.failedDepthBuffer;
}
return false;
}
get frameDelay() {
return (this.settings.browser.powerSaving || this.editingActions) ? (1000 / 45) : 0;
@@ -324,8 +350,16 @@ export class PonyTownGame implements Game {
}
}
private toggleDisableLighting() {
if (!this.failedFBO) {
this.settings.browser.lowGraphicsMode = !this.settings.browser.lowGraphicsMode;
if (!this.webgl) {
return;
}
if (!this.webgl.failedFBO) {
if (this.settings.browser.graphicsQuality === GraphicsQuality.Low) {
this.settings.browser.graphicsQuality = GraphicsQuality.High;
}
else {
this.settings.browser.graphicsQuality = GraphicsQuality.Low;
}
this.settings.saveBrowserSettings();
}
}
@@ -1196,10 +1230,8 @@ export class PonyTownGame implements Game {
this.lastDraw = now;
// draw
const {
gl, frameBuffer, frameBufferSheet, spriteShader, spriteBatch, lightShader, paletteBatch, paletteShader,
palettes,
} = this.webgl;
const { gl, frameBuffer, frameBuffer2, spriteBatch, paletteBatch, palettes, failedFBO,
mergeShader, paletteShader, spriteShader, spriteShaderWithColor, lightShader } = this.webgl;
TIMING && timeStart('draw');
@@ -1244,7 +1276,7 @@ export class PonyTownGame implements Game {
drawOptions.engine = this.engine;
}
ortho(this.fboMatrix, 0, gl.drawingBufferWidth / actualScale, gl.drawingBufferHeight / actualScale, 0, 0, 1000);
ortho(this.fboMatrix, 0, gl.drawingBufferWidth / actualScale, gl.drawingBufferHeight / actualScale, 0, 0, 1000, false);
TIMING && timeEnd();
TIMING && timeStart('ensureAllVisiblePon...');
@@ -1261,133 +1293,135 @@ export class PonyTownGame implements Game {
lerpColor(light, white, 0.3);
}
if (this.engine === Engine.NewLighting) {
// ...
} else if (this.disableLighting) {
ortho(this.viewMatrix, camera.x, camera.x + camera.w, camera.actualY + camera.h, camera.actualY, 0, 1000);
lerpColor(light, white, 0.1); // adjust lighting for missing lights
// you'd draw directly onto the screen only when there's no framebuffer
// or the graphics is low and framebuffer size matches screen size
// color -> screen
gl.clearColor(bg[0], bg[1], bg[2], bg[3]);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.disable(gl.DEPTH_TEST);
gl.enable(gl.BLEND);
gl.blendEquation(gl.FUNC_ADD);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
this.drawMap(this.webgl, this.map, this.viewMatrix, light, drawOptions);
} else {
ortho(this.viewMatrix, camera.x, camera.x + camera.w, camera.actualY, camera.actualY + camera.h, 0, 1000);
TIMING && timeStart('initializeFrameBuffer');
this.initializeFrameBuffer(this.webgl, width, height);
TIMING && timeStart('initializeFrameBuffers');
this.initializeFrameBuffers(this.webgl, width, height, this.settings.browser.graphicsQuality === GraphicsQuality.High);
TIMING && timeEnd();
if (!frameBuffer) {
DEVELOPMENT && console.warn('No frame buffer');
return;
const useDepthBuffer =
!failedFBO && (this.settings.browser.graphicsQuality === GraphicsQuality.High) && !!frameBuffer!.depthStencilRenderbuffer;
const useLighting = !failedFBO && (this.settings.browser.graphicsQuality !== GraphicsQuality.Low);
const drawSceneDirectlyOntoScreen =
failedFBO || (useLighting === false);
drawOptions.useDepthBuffer = useDepthBuffer;
let matrixTop = camera.actualY;
let matrixBottom = camera.actualY + camera.h;
if (!drawSceneDirectlyOntoScreen) {
[matrixTop, matrixBottom] = [matrixBottom, matrixTop];
}
ortho(this.viewMatrix, camera.x, camera.x + camera.w, matrixBottom, matrixTop, 0, 1000, false);
let mapDrawingColor = white;
if (!useLighting) {
mapDrawingColor = light;
lerpColor(mapDrawingColor, white, 0.1); // adjust lighting for missing lights
}
// color -> fbo
TIMING && timeStart('color -> fbo');
// gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer.handle);
bindFrameBuffer(gl, frameBuffer);
gl.viewport(0, 0, frameBuffer.width, frameBuffer.height);
gl.clearColor(bg[0], bg[1], bg[2], bg[3]);
gl.clear(gl.COLOR_BUFFER_BIT); // | gl.DEPTH_BUFFER_BIT);
gl.viewport(0, 0, width, height);
gl.disable(gl.DEPTH_TEST);
//gl.depthFunc(gl.LEQUAL);
gl.enable(gl.BLEND);
gl.blendEquation(gl.FUNC_ADD);
gl.depthFunc(gl.ALWAYS);
if (drawSceneDirectlyOntoScreen) {
TIMING && timeStart('color -> screen');
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
this.drawMap(this.webgl, this.map, this.viewMatrix, white, drawOptions);
this.drawMap(this.webgl, this.map, this.viewMatrix, mapDrawingColor, drawOptions);
TIMING && timeEnd();
}
else {
// if (!isWebGL2(gl)) {
// gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
// gl.clear(gl.COLOR_BUFFER_BIT); // hack, doing this clear fixes black screen issues for some older devices
// }
let clearMask = gl.COLOR_BUFFER_BIT;
if (useDepthBuffer) {
gl.depthMask(true);
clearMask |= gl.DEPTH_BUFFER_BIT;
}
TIMING && timeStart('color -> framebuffer');
bindFrameBuffer(gl, frameBuffer!);
gl.viewport(0, 0, frameBuffer!.width, frameBuffer!.height); // clearing the whole surface is preferable for most GPUs
gl.clear(clearMask);
gl.viewport(0, 0, width, height);
this.drawMap(this.webgl, this.map, this.viewMatrix, mapDrawingColor, drawOptions);
gl.depthMask(false);
TIMING && timeEnd();
// color -> screen
TIMING && timeStart('color -> screen');
// gl.bindFramebuffer(gl.FRAMEBUFFER, null);
if (useLighting) {
TIMING && timeStart('light -> fbo');
bindFrameBuffer(gl, frameBuffer2!);
gl.viewport(0, 0, frameBuffer2!.width, frameBuffer2!.height); // clearing the whole surface is preferable for most GPUs
gl.clearColor(light[0], light[1], light[2], light[3]);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.viewport(0, 0, width, height);
gl.blendFunc(gl.ONE, gl.ONE);
gl.useProgram(lightShader.program);
gl.uniformMatrix4fv(lightShader.uniforms.transform, false, this.viewMatrix);
gl.uniform4fv(lightShader.uniforms.lighting, white);
spriteBatch.begin();
this.lightEntitiesDrawn = drawEntityLights(spriteBatch, this.map.entitiesLight, this.camera, drawOptions);
spriteBatch.end();
if (useDepthBuffer) {
gl.depthFunc(gl.GEQUAL);
}
gl.useProgram(spriteShaderWithColor.program);
gl.uniformMatrix4fv(spriteShaderWithColor.uniforms.transform, false, this.viewMatrix);
gl.uniform4fv(spriteShaderWithColor.uniforms.lighting, white);
gl.uniform2f(spriteShaderWithColor.uniforms.textureSize,
normalSpriteSheet.texture!.width, normalSpriteSheet.texture!.height);
bindTexture(gl, 0, normalSpriteSheet.texture);
spriteBatch.begin();
this.lightSpriteEntitiesDrawn = drawEntityLightSprites(spriteBatch, this.map.entitiesLightSprite, this.camera, drawOptions);
spriteBatch.end();
gl.depthFunc(gl.ALWAYS);
if (isWebGL2(gl)) {
(gl as WebGL2RenderingContext).invalidateFramebuffer(gl.FRAMEBUFFER, [gl.DEPTH_ATTACHMENT]);
}
TIMING && timeEnd();
TIMING && timeStart('color + lights -> screen');
unbindFrameBuffer(gl);
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.disable(gl.BLEND);
gl.useProgram(mergeShader.program);
gl.uniformMatrix4fv(mergeShader.uniforms.transform, false, this.fboMatrix);
gl.uniform2f(mergeShader.uniforms.textureSize, frameBuffer!.colorTexture.width, frameBuffer!.colorTexture.height);
bindTexture(gl, 0, frameBuffer!.colorTexture);
bindTexture(gl, 1, frameBuffer2!.colorTexture);
spriteBatch.begin();
spriteBatch.drawImage(WHITE, 0, 0, width, height, 0, 0, width, height);
spriteBatch.end();
TIMING && timeEnd();
}
else {
TIMING && timeStart('color framebuffer -> screen');
unbindFrameBuffer(gl);
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// gl.disable(gl.DEPTH_TEST);
gl.disable(gl.BLEND);
gl.useProgram(spriteShader.program);
gl.uniformMatrix4fv(spriteShader.uniforms.transform, false, this.fboMatrix);
gl.uniform4fv(spriteShader.uniforms.lighting, white);
gl.uniform1f(spriteShader.uniforms.textureSize, frameBufferSheet.texture!.width);
bindTexture(gl, 0, frameBufferSheet.texture);
spriteBatch.begin();
spriteBatch.drawImage(WHITE, 0, 0, width, height, 0, 0, width, height);
spriteBatch.end();
TIMING && timeEnd();
// light -> fbo
TIMING && timeStart('light -> fbo');
// gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer.handle);
bindFrameBuffer(gl, frameBuffer);
gl.viewport(0, 0, frameBuffer.width, frameBuffer.height);
gl.clearColor(light[0], light[1], light[2], light[3]);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.viewport(0, 0, width, height);
//gl.enable(gl.DEPTH_TEST);
gl.enable(gl.BLEND);
gl.blendEquation(gl.FUNC_ADD);
gl.blendFunc(gl.ONE, gl.ONE);
TIMING && timeEnd();
// shadows
//for (const e of map.entities) {
// if (e.drawShadow && camera.isBoundVisible(e.shadowBounds || e.bounds, e.x, e.y)) {
// this.spriteBatch.depth = camera.mapDepth(e.y);
// e.drawShadow(this.spriteBatch);
// }
//}
// soft lights
TIMING && timeStart('drawEntityLights');
gl.useProgram(lightShader.program);
gl.uniformMatrix4fv(lightShader.uniforms.transform, false, this.viewMatrix);
gl.uniform4fv(lightShader.uniforms.lighting, white);
spriteBatch.begin();
drawEntityLights(spriteBatch, this.map.entitiesLight, this.camera, drawOptions);
spriteBatch.end();
TIMING && timeEnd();
// light sprites
TIMING && timeStart('drawEntityLightSprites');
gl.useProgram(spriteShader.program);
gl.uniformMatrix4fv(spriteShader.uniforms.transform, false, this.viewMatrix);
gl.uniform4fv(spriteShader.uniforms.lighting, white);
gl.uniform1f(spriteShader.uniforms.textureSize, normalSpriteSheet.texture!.width);
bindTexture(gl, 0, normalSpriteSheet.texture);
spriteBatch.begin();
drawEntityLightSprites(spriteBatch, this.map.entitiesLightSprite, this.camera, drawOptions);
spriteBatch.end();
TIMING && timeEnd();
// light -> screen
TIMING && timeStart('light -> screen');
// gl.bindFramebuffer(gl.FRAMEBUFFER, null);
unbindFrameBuffer(gl);
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.enable(gl.BLEND);
gl.blendEquation(gl.FUNC_ADD);
gl.blendFunc(gl.DST_COLOR, gl.ZERO);
gl.useProgram(spriteShader.program);
gl.uniformMatrix4fv(spriteShader.uniforms.transform, false, this.fboMatrix);
gl.uniform4fv(spriteShader.uniforms.lighting, white);
gl.uniform1f(spriteShader.uniforms.textureSize, frameBufferSheet.texture!.width);
bindTexture(gl, 0, frameBufferSheet.texture);
gl.uniform2f(spriteShader.uniforms.textureSize, frameBuffer!.colorTexture.width, frameBuffer!.colorTexture.height);
bindTexture(gl, 0, frameBuffer!.colorTexture);
spriteBatch.begin();
spriteBatch.drawImage(WHITE, 0, 0, width, height, 0, 0, width, height);
spriteBatch.end();
TIMING && timeEnd();
}
}
// ui -> screen
gl.enable(gl.BLEND);
@@ -1399,7 +1433,7 @@ export class PonyTownGame implements Game {
gl.uniformMatrix4fv(paletteShader.uniforms.transform, false, this.fboMatrix);
gl.uniform4fv(paletteShader.uniforms.lighting, white);
gl.uniform1f(paletteShader.uniforms.pixelSize, this.paletteManager.pixelSize);
gl.uniform1f(paletteShader.uniforms.textureSize, paletteSpriteSheet.texture!.width);
gl.uniform1f(paletteShader.uniforms.textureSize, 1.0 / paletteSpriteSheet.texture!.width);
bindTexture(gl, 0, paletteSpriteSheet.texture);
bindTexture(gl, 1, this.paletteManager.texture);
paletteBatch.begin();
@@ -1465,12 +1499,13 @@ export class PonyTownGame implements Game {
paletteBatch.end();
TIMING && timeEnd();
gl.useProgram(spriteShader.program);
gl.uniformMatrix4fv(spriteShader.uniforms.transform, false, this.fboMatrix);
gl.uniform4fv(spriteShader.uniforms.lighting, white);
gl.useProgram(spriteShaderWithColor.program);
gl.uniformMatrix4fv(spriteShaderWithColor.uniforms.transform, false, this.fboMatrix);
gl.uniform4fv(spriteShaderWithColor.uniforms.lighting, white);
if (BETA && this.showMinimap && this.minimap) {
gl.uniform1f(spriteShader.uniforms.textureSize, normalSpriteSheet.texture!.width);
gl.uniform2f(spriteShaderWithColor.uniforms.textureSize,
normalSpriteSheet.texture!.width, normalSpriteSheet.texture!.height);
bindTexture(gl, 0, normalSpriteSheet.texture);
spriteBatch.begin();
spriteBatch.save();
@@ -1498,7 +1533,8 @@ export class PonyTownGame implements Game {
}
if (BETA && this.debug.showRegions && this.player) {
gl.uniform1f(spriteShader.uniforms.textureSize, normalSpriteSheet.texture!.width);
gl.uniform2f(spriteShaderWithColor.uniforms.textureSize,
normalSpriteSheet.texture!.width, normalSpriteSheet.texture!.height);
bindTexture(gl, 0, normalSpriteSheet.texture);
spriteBatch.begin();
drawDebugRegions(spriteBatch, this.map, this.player, this.camera);
@@ -1508,22 +1544,23 @@ export class PonyTownGame implements Game {
const showFPS = !!this.settings.browser.showFps;
const showHelp = BETA && this.input.isPressed(Key.F1);
const showPalette = DEVELOPMENT && this.debug.showPalette;
const showAdditionalStats = false;
if (showFPS || showHelp || showPalette) {
if (showFPS || showHelp || showPalette || showAdditionalStats) {
// 1 to 1 pixel scale drawing
TIMING && timeStart('showFps');
const scale = 2;
// const height = gl.drawingBufferHeight / (ratio * scale);
ortho(this.fboMatrix, 0, gl.drawingBufferWidth / ratio, gl.drawingBufferHeight / ratio, 0, 0, 1000);
gl.uniformMatrix4fv(spriteShader.uniforms.transform, false, this.fboMatrix);
gl.uniform1f(spriteShader.uniforms.textureSize, normalSpriteSheet.texture!.width);
ortho(this.fboMatrix, 0, gl.drawingBufferWidth / ratio, gl.drawingBufferHeight / ratio, 0, 0, 1000, false);
gl.uniformMatrix4fv(spriteShaderWithColor.uniforms.transform, false, this.fboMatrix);
gl.uniform2f(spriteShaderWithColor.uniforms.textureSize,
normalSpriteSheet.texture!.width, normalSpriteSheet.texture!.height);
bindTexture(gl, 0, normalSpriteSheet.texture);
spriteBatch.begin();
spriteBatch.save();
spriteBatch.scale(scale, scale);
if (showFPS) {
drawText(spriteBatch, this.drawFps.toFixed(), fontSmall, BLACK, 2, 2);
drawOutlinedText(spriteBatch, this.drawFps.toFixed(), fontSmall, WHITE, BLACK, 2, 2);
if (this.timingsText) {
const size = measureText(this.timingsText, fontMono);
@@ -1541,19 +1578,18 @@ export class PonyTownGame implements Game {
}
}
// if (DEVELOPMENT) {
// const width = gl.drawingBufferWidth / (ratio * scale);
// const { isCollidingCount, isCollidingObjectCount } = getCollisionStats();
// const text =
// `${isCollidingCount.toString().padStart(7)} calls\n` +
// `${isCollidingObjectCount.toString().padStart(7)} total checks\n` +
// `${this.markedColliding.toString().padStart(7)} player checks`;
// const size = measureText(text, fontMono);
// const x = width - 160;
// const y = 26;
// spriteBatch.drawRect(0x000000aa, x, y, 150, size.h + 10);
// drawText(spriteBatch, text, fontMono, WHITE, x + 5, y + 5);
// }
if (showAdditionalStats) {
const width = gl.drawingBufferWidth / (ratio * scale);
const text =
`entities ${this.entitiesDrawn}/${this.map.entitiesDrawable.length}\n` +
`light enitities ${this.lightEntitiesDrawn}/${this.map.entitiesLight.length}\n` +
`light sprite entities ${this.lightSpriteEntitiesDrawn}/${this.map.entitiesLightSprite.length}`;
const size = measureText(text, fontMono);
const x = width - Math.max(160, size.w);
const y = 26;
spriteBatch.drawRect(0x000000aa, x, y, 150, size.h + 10);
drawText(spriteBatch, text, fontMono, WHITE, x + 5, y + 5);
}
spriteBatch.restore();
spriteBatch.end();
@@ -1562,13 +1598,14 @@ export class PonyTownGame implements Game {
const paletteTexture = this.paletteManager.texture!;
const { width, height } = paletteTexture;
gl.uniform1f(spriteShader.uniforms.textureSize, normalSpriteSheet.texture!.width);
gl.uniform2f(spriteShaderWithColor.uniforms.textureSize,
normalSpriteSheet.texture!.width, normalSpriteSheet.texture!.height);
bindTexture(gl, 0, normalSpriteSheet.texture);
spriteBatch.begin();
spriteBatch.drawRect(0x00000066, 20, 20, width, height);
spriteBatch.end();
gl.uniform1f(spriteShader.uniforms.textureSize, width);
gl.uniform2f(spriteShaderWithColor.uniforms.textureSize, width, height);
bindTexture(gl, 0, paletteTexture);
spriteBatch.begin();
spriteBatch.drawImage(WHITE, 0, 0, width, height, 20, 20, width, height);
@@ -1599,15 +1636,17 @@ export class PonyTownGame implements Game {
drawFullScreenMessage(paletteBatch, this.camera, message, palettes.mainFont.white);
}
private drawMap(webgl: WebGL, map: WorldMap, viewMatrix: Matrix4, lighting: Float32Array, options: DrawOptions) {
const { gl, paletteBatch, paletteShader } = webgl;
const { gl, paletteBatch, paletteShader, paletteShaderWithDepth } = webgl;
const mapPaletteShader = options.useDepthBuffer ? paletteShaderWithDepth : paletteShader;
TIMING && timeStart('drawMap');
if (this.tileSets && this.player) {
gl.useProgram(paletteShader.program);
gl.uniformMatrix4fv(paletteShader.uniforms.transform, false, viewMatrix);
gl.uniform4fv(paletteShader.uniforms.lighting, lighting);
gl.uniform1f(paletteShader.uniforms.pixelSize, this.paletteManager.pixelSize);
gl.uniform1f(paletteShader.uniforms.textureSize, paletteSpriteSheet.texture!.width);
gl.useProgram(mapPaletteShader.program);
gl.uniformMatrix4fv(mapPaletteShader.uniforms.transform, false, viewMatrix);
gl.uniform4fv(mapPaletteShader.uniforms.lighting, lighting);
gl.uniform1f(mapPaletteShader.uniforms.pixelSize, this.paletteManager.pixelSize);
gl.uniform1f(mapPaletteShader.uniforms.textureSize, 1.0 / paletteSpriteSheet.texture!.width);
bindTexture(gl, 0, paletteSpriteSheet.texture);
bindTexture(gl, 1, this.paletteManager.texture);
paletteBatch.begin();
@@ -1616,7 +1655,7 @@ export class PonyTownGame implements Game {
paletteBatch.end();
if (this.highlightEntity && this.highlightEntity.draw) {
gl.uniform4fv(paletteShader.uniforms.lighting, highlightColor);
gl.uniform4fv(mapPaletteShader.uniforms.lighting, highlightColor);
paletteBatch.begin();
this.highlightEntity.draw(paletteBatch, this.drawOptions);
paletteBatch.end();
@@ -1678,16 +1717,29 @@ export class PonyTownGame implements Game {
DEVELOPMENT && log(`scrollY: ${window.scrollY}`);
}
}
private initializeFrameBuffer({ gl, frameBuffer }: WebGL, width: number, height: number) {
const targetSize = getRenderTargetSize(width, height);
private initializeFrameBuffers(
{ gl, frameBuffer, frameBuffer2 }: WebGL, width: number, height: number, useDepthBuffer: boolean
) {
if (!frameBuffer || !frameBuffer2) {
return;
}
if (frameBuffer && targetSize !== frameBuffer.width) {
const hasDepth = frameBuffer.depthStencilRenderbuffer !== null;
const isSizeChanged = width !== frameBuffer.width || height !== frameBuffer.height;
const isDepthChanged = hasDepth !== useDepthBuffer;
if (isSizeChanged || isDepthChanged) {
const maxSize = gl.getParameter(gl.MAX_RENDERBUFFER_SIZE);
const isSizeTooBig = maxSize != null && (width > maxSize || height > maxSize);
if (maxSize != null && targetSize > maxSize) {
this.setScale(this.scale + 1);
if (isSizeTooBig) {
this.setScale(this.scale + 1); // should not happen, useDepthBuffer is also ignored if it does
DEVELOPMENT && console.warn('Cannot resize framebuffer');
} else {
resizeFrameBuffer(gl, frameBuffer, targetSize, targetSize);
disposeFrameBuffer(gl, frameBuffer);
disposeFrameBuffer(gl, frameBuffer2);
createFrameBuffer(gl, frameBuffer, width, height, false, useDepthBuffer, null);
createFrameBuffer(gl, frameBuffer2, width, height, false, false, frameBuffer.depthStencilRenderbuffer);
}
}
}
@@ -1740,7 +1792,7 @@ export class PonyTownGame implements Game {
let value = '';
if (this.settings.browser.showStats) {
const tris = spriteBatch.tris + paletteBatch.tris;
const tris = spriteBatch.drawnTrisStats + paletteBatch.drawnTrisStats;
const flush = paletteBatch.flushes;
const sent = this.sent.toFixed();
const recv = this.recv.toFixed();
@@ -1770,9 +1822,9 @@ export class PonyTownGame implements Game {
TIMING && timeReset();
spriteBatch!.tris = 0;
spriteBatch!.drawnTrisStats = 0;
spriteBatch!.flushes = 0;
paletteBatch!.tris = 0;
paletteBatch!.drawnTrisStats = 0;
paletteBatch!.flushes = 0;
}
}
+24
View File
@@ -522,6 +522,12 @@ export const excite = createHeadAnimation('excite', 8, false, [
...repeat(10, [0, 0, 1, 1, 5]),
]);
export const excite_meno = createHeadAnimation('excite_meno', 8, false, [
...repeat(2, [0, 0, 2, 2, 0]),
[0, 1, 6, 6, 0],
...repeat(10, [0, 0, 1, 1, 5])
]);
export const surpriseSad = createHeadAnimation('surpriseSad', 8, false, [
[0, 1, 15, 15, 8],
...repeat(8, [0, 0, 15, 15, 8]),
@@ -534,6 +540,24 @@ export const sneeze = createHeadAnimation('sneeze', 12, false, [
...repeat(4, [0, 0, 18, 18, 7]),
]);
export const happy_tongue = createHeadAnimation('happy_tongue', 12, false, [
...repeat(3, [0, 0, 1, 1, 0]),
[0, 1, 6, 6, 0],
...repeat(8, [0, 0, 14, 14, 4]),
]);
export const happy_tongue_meno = createHeadAnimation('happy_tongue_meno', 12, false, [
...repeat(3, [0, 0, 2, 2, 0]),
[0, 1, 6, 6, 0],
...repeat(8, [0, 0, 12, 12, 4]),
]);
export const happy_tongue_meno_2 = createHeadAnimation('happy_tongue_meno_2', 12, false, [
...repeat(3, [0, 0, 2, 2, 0]),
[0, 1, 6, 6, 0],
...repeat(8, [0, 0, 11, 11, 4]),
]);
export const headAnimations = [
smile, nom, laugh, yawn, surprise, surpriseSad, sneeze, excite,
];
+10 -7
View File
@@ -214,13 +214,14 @@ export function initializeToys(paletteManager: PaletteManager) {
}
}
const zeroPoint = point(0, 0);
// const zeroPoint = point(0, 0);
const wakes = [
{ ox: 21, oy: 60, behind: sprites.pony_wake_4, front: sprites.pony_wake_3 },
{ ox: 24, oy: 60, behind: sprites.pony_wake_6, front: sprites.pony_wake_5 },
{ ox: 18, oy: 51, behind: sprites.pony_wake_2, front: sprites.pony_wake_1 },
];
// swimming effects of different size; add value per tail
const wakeIndices = [0, 2, 1, 0, 2, 2, 2, 0, 2, 2, 2, 1, 2, 2, 2, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 1];
function getWakeIndex(info: Info) {
@@ -292,9 +293,10 @@ export function drawPony(batch: Batch, info: Info, state: State, ponyX: number,
let hatOffset = at(HEAD_ACCESSORY_OFFSETS, info.mane ? info.mane.type : 0)!;
const noMane = !info.mane || hasNoMane(info.mane.type);
if (info.headAccessory !== undefined && info.headAccessory.type === 20) {
hatOffset = zeroPoint;
}
// temp front layer hat test
// if (info.headAccessory !== undefined && info.headAccessory.type === 20) {
// hatOffset = zeroPoint;
//}
if (draw(options, NoDraw.Behind)) {
// far wing
@@ -639,13 +641,14 @@ export function drawHead(
drawSet(batch, sprites.facialHair, info.facialHair, x, y, WHITE);
}
const skipTopAndFrontMane = info.headAccessory !== undefined && info.headAccessory.type === 20;
// temp front layer hat test
// const skipTopAndFrontMane = info.headAccessory !== undefined && info.headAccessory.type === 20;
if (draw(options, NoDraw.FrontMane)) {
drawSet(batch, sprites.backFrontManes, info.backMane, x, y + maneOffsetY, WHITE);
}
if (draw(options, NoDraw.TopMane) && !skipTopAndFrontMane) {
if (draw(options, NoDraw.TopMane) /* && !skipTopAndFrontMane*/ ) {
drawSet(batch, sprites.topManes, info.mane, x, y, WHITE);
}
@@ -682,7 +685,7 @@ export function drawHead(
drawSet(batch, sprites.earAccessories, info.earAccessory, x + earAccessoryOffset.x, y + earAccessoryOffset.y, WHITE);
}
if (draw(options, NoDraw.FrontMane) && !skipTopAndFrontMane) {
if (draw(options, NoDraw.FrontMane) /* && !skipTopAndFrontMane */) {
drawSet(batch, sprites.frontManes, info.mane, x, y + maneOffsetY, WHITE);
}
-5
View File
@@ -39,7 +39,6 @@ export const tails = createCompleteSets(sprites.tails, 3);
export const chest = createCompleteSets(sprites.chestAccessories, bodyFrames);
export const chestBehind = createCompleteSets(sprites.chestAccessoriesBehind, bodyFrames);
export const backAccessories = createCompleteSets(sprites.backAccessories, bodyFrames);
sprites.neckAccessories.forEach(f => f && f.pop()); // TEMP: remove headphones
export const neckAccessories = createCompleteSets(sprites.neckAccessories, bodyFrames);
export const waistAccessories = createCompleteSets(sprites.waistAccessories, bodyFrames + 1);
@@ -145,10 +144,6 @@ function mergeSpriteSets(...sets: ColorExtraSets[]): ColorExtraSets {
export const backLegSleeves: Sets = sprites.backLegSleeves
.map(sets => sets && [undefined, undefined, undefined, undefined, undefined, ...sets]);
// TEMP: remove summer hat
sprites.headAccessoriesBehind.pop();
sprites.headAccessories.pop();
export const mergedManes = mergeSpriteSets(sprites.behindManes, sprites.topManes, sprites.frontManes)!;
export const mergedBackManes = mergeSpriteSets(sprites.backBehindManes, sprites.backFrontManes)!;
export const mergedFacialHair = mergeSpriteSets(sprites.facialHairBehind, sprites.facialHair)!;
+57 -54
View File
@@ -1,35 +1,41 @@
import { spriteShader, paletteLayersShader, lightShader } from '../generated/shaders';
import { FrameBuffer, createFrameBuffer, disposeFrameBuffer } from '../graphics/webgl/frameBuffer';
import { SpriteSheet, CommonPalettes, PaletteManager, Camera } from '../common/interfaces';
import { Shader, createShader, disposeShader } from '../graphics/webgl/shader';
import { PaletteSpriteBatch, PALETTE_BATCH_BYTES_PER_VERTEX } from '../graphics/paletteSpriteBatch';
import { getWebGLContext, getRenderTargetSize, unbindAllTexturesAndBuffers } from '../graphics/webgl/webglUtils';
import { mergeShader, spriteShader, paletteLayersShader, lightShader } from '../generated/shaders';
import { FrameBuffer, disposeFrameBuffer, createFrameBuffer } from '../graphics/webgl/frameBuffer';
import { CommonPalettes, PaletteManager, Camera } from '../common/interfaces';
import { Shader, ShaderProgramData, disposeShaderProgramData } from '../graphics/webgl/shader';
import { PaletteSpriteBatch } from '../graphics/paletteSpriteBatch';
import { getWebGLContext, unbindAllTexturesAndBuffers } from '../graphics/webgl/webglUtils';
import { createCommonPalettes } from '../graphics/graphicsUtils';
import { createTexturesForSpriteSheets, disposeTexturesForSpriteSheets } from '../graphics/spriteSheetUtils';
import * as sprites from '../generated/sprites';
import { SpriteBatch } from '../graphics/spriteBatch';
import { BATCH_SIZE_MAX } from '../common/constants';
import { BATCH_VERTEX_CAPACITY_MAX } from '../common/constants';
export interface WebGL {
gl: WebGLRenderingContext;
frameBuffer: FrameBuffer | undefined;
frameBufferSheet: SpriteSheet;
spriteShader: Shader;
lightShader: Shader;
paletteShader: Shader;
frameBuffer?: FrameBuffer;
frameBuffer2?: FrameBuffer;
mergeShader: ShaderProgramData;
paletteShader: ShaderProgramData;
paletteShaderWithDepth: ShaderProgramData;
spriteShader: ShaderProgramData;
spriteShaderWithColor: ShaderProgramData;
lightShader: ShaderProgramData;
spriteBatch: SpriteBatch;
paletteBatch: PaletteSpriteBatch;
palettes: CommonPalettes;
failedFBO: boolean;
failedDepthBuffer: boolean;
renderer: string;
indexBuffer: WebGLBuffer;
}
const mergeShaderSource = mergeShader;
const spriteShaderSource = spriteShader;
const paletteShaderSource = paletteLayersShader;
const lightShaderSource = lightShader;
function createIndices(capacity: number) {
const numIndices = (capacity * 6) | 0;
function createIndices(vertexCount: number) {
const numIndices = (vertexCount * 6 / 4) | 0;
const indices = new Uint16Array(numIndices);
for (let i = 0, j = 0; i < numIndices; j = (j + 4) | 0) {
@@ -52,62 +58,48 @@ export function initWebGL(canvas: HTMLCanvasElement, paletteManager: PaletteMana
export function initWebGLResources(gl: WebGLRenderingContext, paletteManager: PaletteManager, camera: Camera): WebGL {
let renderer = '';
let failedFBO = false;
let frameBuffer: FrameBuffer | undefined;
let frameBufferSheet: SpriteSheet = { texture: undefined, sprites: [], palette: false };
let failedDepthBuffer = false;
let frameBuffer = {} as FrameBuffer;
let frameBuffer2 = {} as FrameBuffer;
gl.enable(gl.DEPTH_TEST); // no reason to not have it enabled at all times, depth reads/writes are controlled by other parameters
gl.disable(gl.DITHER);
try {
const size = getRenderTargetSize(camera.w, camera.h);
frameBuffer = createFrameBuffer(gl, size, size);
frameBufferSheet.texture = frameBuffer.texture;
createFrameBuffer(gl, frameBuffer, camera.w, camera.h, false, true, null);
createFrameBuffer(gl, frameBuffer2, camera.w, camera.h, true, false, frameBuffer.depthStencilRenderbuffer);
} catch (e) {
DEVELOPMENT && console.warn(e);
failedFBO = true;
failedDepthBuffer = true;
}
if (!failedFBO) {
failedDepthBuffer = frameBuffer!.depthStencilRenderbuffer === null;
}
createTexturesForSpriteSheets(gl, sprites.spriteSheets);
const palettes = createCommonPalettes(paletteManager);
const paletteShader = createShader(gl, paletteShaderSource);
const spriteShader = createShader(gl, spriteShaderSource);
const lightShader = createShader(gl, lightShaderSource);
const VERTICES_PER_SPRITE = 4;
const buffer = new ArrayBuffer(BATCH_SIZE_MAX * VERTICES_PER_SPRITE * PALETTE_BATCH_BYTES_PER_VERTEX);
const vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
throw new Error(`Failed to allocate vertex buffer`);
}
const mergeShaderRaw = new Shader(mergeShaderSource);
const paletteShaderRaw = new Shader(paletteShaderSource);
const spriteShaderRaw = new Shader(spriteShaderSource);
const lightShaderRaw = new Shader(lightShaderSource);
const indexBuffer = gl.createBuffer();
if (!indexBuffer) {
throw new Error(`Failed to allocate index buffer`);
}
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, buffer, gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, createIndices(BATCH_SIZE_MAX), gl.STATIC_DRAW);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, createIndices(BATCH_VERTEX_CAPACITY_MAX), gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
const vertexBuffer2 = gl.createBuffer();
if (!vertexBuffer2) {
throw new Error(`Failed to allocate vertex buffer (2)`);
}
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer2);
gl.bufferData(gl.ARRAY_BUFFER, buffer, gl.STATIC_DRAW);
const spriteBatch = new SpriteBatch(gl, BATCH_SIZE_MAX, buffer, vertexBuffer2, indexBuffer);
const paletteBatch = new PaletteSpriteBatch(gl, BATCH_SIZE_MAX, buffer, vertexBuffer, indexBuffer);
const spriteBatch = new SpriteBatch(gl, BATCH_VERTEX_CAPACITY_MAX, indexBuffer);
const paletteBatch = new PaletteSpriteBatch(gl, BATCH_VERTEX_CAPACITY_MAX, indexBuffer);
spriteBatch.rectSprite = sprites.pixel;
paletteBatch.rectSprite = sprites.pixel2;
paletteBatch.defaultPalette = palettes.defaultPalette;
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
paletteManager.init(gl);
const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
@@ -116,9 +108,17 @@ export function initWebGLResources(gl: WebGLRenderingContext, paletteManager: Pa
renderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
}
const mergeShader = mergeShaderRaw.compile(gl, []);
const paletteShader = paletteShaderRaw.compile(gl, []);
const paletteShaderWithDepth = paletteShaderRaw.compile(gl, ['DEPTH_BUFFERED']);
const spriteShader = spriteShaderRaw.compile(gl, []);
const spriteShaderWithColor = spriteShaderRaw.compile(gl, ['USE_COLOR']);
const lightShader = lightShaderRaw.compile(gl, []);
return {
gl, paletteShader, spriteShader, lightShader, spriteBatch, paletteBatch,
frameBuffer, frameBufferSheet, palettes, failedFBO, renderer,
gl, mergeShader: mergeShader, paletteShader: paletteShader, paletteShaderWithDepth: paletteShaderWithDepth,
spriteShader: spriteShader, spriteShaderWithColor: spriteShaderWithColor, lightShader: lightShader,
spriteBatch, paletteBatch, frameBuffer, frameBuffer2, palettes, failedFBO, failedDepthBuffer, renderer, indexBuffer
};
}
@@ -128,9 +128,12 @@ export function disposeWebGL(webgl: WebGL) {
unbindAllTexturesAndBuffers(gl);
disposeTexturesForSpriteSheets(gl, sprites.spriteSheets);
disposeFrameBuffer(gl, webgl.frameBuffer);
disposeShader(gl, webgl.lightShader);
disposeShader(gl, webgl.spriteShader);
disposeShader(gl, webgl.paletteShader);
disposeShaderProgramData(gl, webgl.paletteShader);
disposeShaderProgramData(gl, webgl.paletteShaderWithDepth);
disposeShaderProgramData(gl, webgl.spriteShader);
disposeShaderProgramData(gl, webgl.spriteShaderWithColor);
disposeShaderProgramData(gl, webgl.lightShader);
webgl.spriteBatch.dispose();
webgl.paletteBatch.dispose();
gl.deleteBuffer(webgl.indexBuffer);
}
+33 -25
View File
@@ -14,7 +14,20 @@ import {
} from '../client/ponyUtils';
import { CM_SIZE } from './constants';
export const VERSION = 4;
export const VERSION = 5; // previous: 3
const VERSION_BITS = 6; // max 63
const COLORS_LENGTH_BITS = 10; // max 1024
const BOOLEAN_FIELDS_LENGTH_BITS = 4; // max 15
const NUMBER_FIELDS_LENGTH_BITS = 4; // max 15
const COLOR_FIELDS_LENGTH_BITS = 4; // max 15
const SET_FIELDS_LENGTH_BITS = 6; // max 63
const CM_LENGTH_BITS = 5; // max 31
const NUMBERS_BITS = 6; // max 63
const SET_TYPE_BITS = 6; // max 63
const SET_PATTERN_BITS = 4; // max 15
const SET_COLORS_BITS = 3; // max 7
interface FieldDefinition<T> {
name: keyof PonyInfo;
@@ -181,15 +194,6 @@ const omittableFields: FieldDefinition<any>[] = [
...colorFields,
].filter(f => !!f.omit);
const VERSION_BITS = 6; // max 63
const COLORS_LENGTH_BITS = 10; // max 1024
const BOOLEAN_FIELDS_LENGTH_BITS = 4; // max 15
const NUMBER_FIELDS_LENGTH_BITS = 4; // max 15
const COLOR_FIELDS_LENGTH_BITS = 4; // max 15
const SET_FIELDS_LENGTH_BITS = 6; // max 63
const CM_LENGTH_BITS = 5; // max 31
const NUMBERS_BITS = 6; // max 63
/* istanbul ignore next */
if (DEVELOPMENT) {
(function () {
@@ -475,17 +479,15 @@ export function postdecompressPony<T>(data: Precompressed, parseColor: (color: n
// set
const TYPE_BITS = 5; // max 31
const PATTERN_BITS = 4; // max 15
const COLORS_BITS = 3; // max 7
export function writeSet(write: WriteBits, colorBits: number, customOutlines: boolean, set: PrecompressedSet | undefined) {
export function writeSet(
write: WriteBits, typeBits: number, colorBits: number, customOutlines: boolean, set: PrecompressedSet | undefined
) {
write(set ? 1 : 0, 1);
if (set) {
write(set.type, TYPE_BITS);
write(set.pattern, PATTERN_BITS);
write(set.colors - 1, COLORS_BITS);
write(set.type, typeBits);
write(set.pattern, SET_PATTERN_BITS);
write(set.colors - 1, SET_COLORS_BITS);
write(set.fillLocks, set.colors);
set.fills.forEach(c => write(c, colorBits));
@@ -496,13 +498,15 @@ export function writeSet(write: WriteBits, colorBits: number, customOutlines: bo
}
}
export function readSet(read: ReadBits, colorBits: number, customOutlines: boolean): PrecompressedSet | undefined {
export function readSet(
read: ReadBits, typeBits: number, colorBits: number, customOutlines: boolean
): PrecompressedSet | undefined {
const has = read(1);
if (has) {
const type = read(TYPE_BITS);
const pattern = read(PATTERN_BITS);
const colors = read(COLORS_BITS) + 1;
const type = read(typeBits);
const pattern = read(SET_PATTERN_BITS);
const colors = read(SET_COLORS_BITS) + 1;
const fillLocks = read(colors);
const fills = readTimes(read, colors - countBits(fillLocks), colorBits);
const outlineLocks = customOutlines ? read(colors) : 0;
@@ -536,12 +540,14 @@ function readFields<T>(read: ReadBits, lengthBits: number, readField: (read: Rea
export function writePony(write: WriteBits, data: Precompressed) {
const colorBits = Math.max(numberToBitCount(data.colors.length), 1);
const customOutlines = !!data.booleanFields[0];
const typeBits = data.version < 5 ? 5 : SET_TYPE_BITS;
write(data.version, VERSION_BITS);
writeFields(write, COLORS_LENGTH_BITS, data.colors, x => write(x >> 8, 24));
writeFields(write, BOOLEAN_FIELDS_LENGTH_BITS, data.booleanFields, x => write(x ? 1 : 0, 1));
writeFields(write, NUMBER_FIELDS_LENGTH_BITS, data.numberFields, x => write(x, NUMBERS_BITS));
writeFields(write, COLOR_FIELDS_LENGTH_BITS, data.colorFields, x => write(x, colorBits));
writeFields(write, SET_FIELDS_LENGTH_BITS, data.setFields, x => writeSet(write, colorBits, customOutlines, x));
writeFields(write, data.version < 4 ? 5 : SET_FIELDS_LENGTH_BITS, data.setFields,
x => writeSet(write, typeBits, colorBits, customOutlines, x));
writeFields(write, CM_LENGTH_BITS, data.cm, x => write(x, colorBits));
}
@@ -553,10 +559,11 @@ const readNumber = readBits(NUMBERS_BITS);
export function readPony(read: ReadBits): Precompressed {
const version = read(VERSION_BITS);
if (version > VERSION) {
if (version > 5) {
throw new Error('Invalid version');
}
const typeBits = version < 5 ? 5 : SET_TYPE_BITS;
const colors = readFields(read, COLORS_LENGTH_BITS, readColorValue);
const colorBits = Math.max(numberToBitCount(colors.length), 1);
const readColor = readBits(colorBits);
@@ -564,7 +571,8 @@ export function readPony(read: ReadBits): Precompressed {
const customOutlines = !!booleanFields[0];
const numberFields = readFields(read, NUMBER_FIELDS_LENGTH_BITS, readNumber);
const colorFields = readFields(read, COLOR_FIELDS_LENGTH_BITS, readColor);
const setFields = readFields(read, version < 4 ? 5 : SET_FIELDS_LENGTH_BITS, read => readSet(read, colorBits, customOutlines));
const setFields = readFields(read, version < 4 ? 5 : SET_FIELDS_LENGTH_BITS,
read => readSet(read, typeBits, colorBits, customOutlines));
const cm = readFields(read, CM_LENGTH_BITS, readColor);
return { version, colors, booleanFields, numberFields, colorFields, setFields, cm };
}
+6 -2
View File
@@ -11,10 +11,12 @@ export const WEEK = DAY * 7;
export const MONTH = DAY * 30;
export const YEAR = DAY * 365;
export const BATCH_SIZE_MAX = 10000;
export const BATCH_VERTEX_CAPACITY_MAX = 16384;
export const MAX_VELOCITY = 16; // do not change
export const NEW_ACCOUNT_PONY_NAME = 'Pony';
export const PONY_TYPE = 1;
export const PONY_SPEED_TROT = 4; // tiles per sec
export const PONY_SPEED_WALK = 2; // tiles per sec
@@ -37,6 +39,8 @@ export const MAP_SWITCH_DELAY = 1 * SECOND;
export const MAP_SWITCHES_PER_UPDATE = 1;
export const JOINS_PER_UPDATE = 1;
export const LIGHT_VOLUME_SCALE = 0.81;
export const DEFAULT_CHATLOG_OPACITY = 35;
export const MAX_CHATLOG_RANGE = 11;
export const MIN_CHATLOG_RANGE = 2;
@@ -183,7 +187,7 @@ export const PAST_SUPPORTER_REWARDS = [
export const GENERAL_RULES = [
`Be kind to others`,
`Don't spam`,
`Don't use multiple accounts`,
`Don't create multiple accounts`,
`Don't modify the game with hacks or scripts`,
`Don't encourage behaviour violating the rules`,
`Violation of the rules may result in temporary or permanent ban`,
+5 -7
View File
@@ -22,7 +22,7 @@ import { mockPaletteManager } from './ponyInfo';
const entities: EntityDescriptor[] = [];
export function createBaseEntity(type: number, id: number, x: number, y: number): Entity {
return { id, type, x, y, z: 0, vx: 0, vy: 0, order: 0, state: 0, playerState: 0, flags: 0, timestamp: 0 };
return { id, type, x, y, z: 0, vx: 0, vy: 0, depth: 0, order: 0, state: 0, playerState: 0, flags: 0, timestamp: 0 };
}
function createEntity(
@@ -154,12 +154,14 @@ function mixOrder(order: number): MixinEntity {
const collectableInteractive = mixInteract(-8, -12, 16, 16, 1.5);
// entity with centered sprite and larger clickable area
function collectable(name: string, sprite: PaletteRenderable, paletteIndex = 0, ...other: MixinEntity[]) {
return doodad(name, sprite, Math.floor(sprite.color!.w / 2), sprite.color!.h - 1, paletteIndex,
collectableInteractive,
...other);
}
// for ground details. puts origin point on the top of sprite, so you always go on top of it. collectables are able to spawn overlapping decals
function decal(name: string, sprite: PaletteRenderable, palette = 0, ...other: MixinEntity[]) {
return registerMix(name,
mixDraw(sprite, Math.floor((sprite.color!.w + sprite.color!.ox) / 2), sprite.color!.oy, palette),
@@ -174,6 +176,7 @@ function decalOffset(name: string, sprite: PaletteRenderable, dx: number, dy: nu
...other);
}
// for decorative objects
function doodad(
name: string, sprite: PaletteRenderable, ox: number, oy: number, palatte = 0, ...other: (MixinEntity | undefined)[]
) {
@@ -807,11 +810,6 @@ export const rock3B = doodad(n('rock-3b'), sprites.rock_3, 10, 11, 1,
mixColliderRounded(-10, -4, 18, 5, 2, false),
rockMinimap);
// other
export const well = doodad(n('well'), sprites.well, 30, 67, 0,
mixColliderRect(-26, -20, 54, 30));
// water rocks
const waterRockFPS = WATER_FPS;
@@ -1552,7 +1550,7 @@ const cloudSprite = sprites.cloud.shadow;
export const cloud = registerMix(n('cloud'),
mixDrawShadow(sprites.cloud, Math.floor(cloudSprite.w / 2), cloudSprite.h, CLOUD_SHADOW_COLOR),
mixFlags(EntityFlags.Decal | EntityFlags.Movable));
mixFlags(EntityFlags.StaticY | EntityFlags.Decal | EntityFlags.Movable));
// vegetation
+13 -1
View File
@@ -93,6 +93,7 @@ export type Batch = Float32Array;
export interface SpriteBatchBase {
globalAlpha: number;
depth: number;
crop(x: number, y: number, w: number, h: number): void;
clearCrop(): void;
save(): void;
@@ -105,11 +106,11 @@ export interface SpriteBatchBase {
startBatch(): void;
finishBatch(): Batch | undefined;
releaseBatch(batch: Batch): void;
flush(): void;
}
export interface SpriteBatchCommons extends SpriteBatchBase {
palette: boolean;
depth?: number;
drawRect(color: number, x: number, y: number, w: number, h: number): void;
}
@@ -360,6 +361,7 @@ export interface SpriteSheet {
texture: Texture2D | undefined;
sprites: (Sprite | undefined)[];
palette: boolean;
isSingleChannel: boolean;
}
export interface SpriteBorder {
@@ -541,6 +543,7 @@ export interface Entity extends EntityPart {
z: number;
vx: number;
vy: number;
depth: number;
// frame: number; // last update frame
timestamp: number;
@@ -668,8 +671,15 @@ export interface AccountSettings {
hidden?: boolean;
}
export const enum GraphicsQuality {
Low,
Medium,
High
}
export interface BrowserSettings {
lowGraphicsMode?: boolean;
graphicsQuality?: GraphicsQuality;
chatlogClosed?: boolean;
chatlogTab?: string;
chatlogWidth?: number;
@@ -1639,6 +1649,7 @@ export interface DrawOptions {
tileGrid: boolean;
engine: Engine;
season: Season;
useDepthBuffer: boolean;
error: (message: string) => void;
}
@@ -1655,6 +1666,7 @@ export const defaultDrawOptions: DrawOptions = {
tileGrid: false,
engine: Engine.Default,
season: Season.Summer,
useDepthBuffer: true,
error: () => { },
};
+13 -17
View File
@@ -9,25 +9,21 @@ export function createMat4(): Matrix4 {
return out;
}
export function ortho(out: Matrix4, left: number, right: number, bottom: number, top: number, near: number, far: number) {
export function ortho(out: Matrix4, left: number, right: number, bottom: number, top: number,
near: number, far: number, transformZ: boolean
) {
const lr = 1 / (left - right);
const bt = 1 / (bottom - top);
const nf = 1 / (near - far);
out[0] = -2 * lr;
out[1] = 0;
out[2] = 0;
out[3] = 0;
out[4] = 0;
out[5] = -2 * bt;
out[6] = 0;
out[7] = 0;
out[8] = 0;
out[9] = 0;
out[10] = 2 * nf;
out[11] = 0;
out[12] = (left + right) * lr;
out[13] = (top + bottom) * bt;
out[14] = (far + near) * nf;
out[15] = 1;
out[0] = -2 * lr; out[1] = 0; out[2] = 0; out[3] = 0;
out[4] = 0; out[5] = -2 * bt; out[6] = 0; out[7] = 0;
out[8] = 0; out[9] = 0; out[10] = 2 * nf; out[11] = 0;
out[12] = (left + right) * lr; out[13] = (top + bottom) * bt; out[14] = (far + near) * nf; out[15] = 1;
if (!transformZ) {
out[10] = 1;
out[14] = 0;
}
return out;
}
+5 -5
View File
@@ -9,7 +9,7 @@ import { at, att, hasFlag, invalidEnum } from './utils';
import { WHITE, BLACK, RED } from './colors';
import { toScreenX, toWorldX, toWorldY, toScreenYWithZ } from './positionUtils';
import { rect, addRects, addRect } from './rect';
import { SECOND } from './constants';
import { SECOND, LIGHT_VOLUME_SCALE } from './constants';
import { mockPaletteManager } from './ponyInfo';
import { releasePalette } from '../graphics/paletteManager';
@@ -679,16 +679,16 @@ export function mixLight(color: number, dx: number, dy: number, w: number, h: nu
base.lightScale = 1;
base.lightTarget = 1;
base.lightScaleAdjust = 1;
base.lightBounds = rect(-(dx + w / 2), -(dy + h / 2), w, h);
const adjustedScale = base.lightScale * base.lightScaleAdjust * LIGHT_VOLUME_SCALE;
base.lightBounds = rect(-(dx + w / 2), -(dy + h / 2), w * adjustedScale, h * adjustedScale);
base.drawLight = function (batch: SpriteBatch) {
if (!this.lightOn)
return;
const x = toScreenX(this.x);
const y = toScreenYWithZ(this.y, this.z);
const s = this.lightScale! * this.lightScaleAdjust!;
const width = w * s;
const height = h * s;
const width = w * adjustedScale;
const height = h * adjustedScale;
const color = this.lightColor!;
batch.drawImage(color, -1, -1, 2, 2, x - (dx + width / 2), y - (dy + height / 2), width, height);
+1
View File
@@ -71,6 +71,7 @@ export function createPony(
z: 0,
vx: 0,
vy: 0,
depth: 0,
info,
order: 0,
timestamp: 0,
@@ -125,20 +125,20 @@ div(*ngIf="account")
button.dropdown-item((click)="unmerge(m._id, m.data.account, m.data.merge)")
| Split off #[b account]
h5 Support #[span.text-muted ({{support?.length || 0}})]
table.table.table-sm(*ngIf="support?.length")
thead
tr
th message
th date
tbody
tr(*ngFor="let l of support")
td
fa-icon.mr-1([icon]="l.icon" [ngClass]="l.class")
| {{l.message}}
td
time-field.mr-2([time]="l.date")
span.text-muted ({{l.date | date:'MMM d'}})
//- h5 Support #[span.text-muted ({{support?.length || 0}})]
//- table.table.table-sm(*ngIf="support?.length")
//- thead
//- tr
//- th message
//- th date
//- tbody
//- tr(*ngFor="let l of support")
//- td
//- fa-icon.mr-1([icon]="l.icon" [ngClass]="l.class")
//- | {{l.message}}
//- td
//- time-field.mr-2([time]="l.date")
//- span.text-muted ({{l.date | date:'MMM d'}})
h5 Ban log #[span.text-muted ({{banLog?.length || 0}})]
table.table.table-sm(*ngIf="banLog?.length")
@@ -38,9 +38,6 @@ ng-template(#duplicatesPopover)
span.ml-3(title="Certificate expires in" [class.text-danger]="isOldCertificate")
fa-icon.mr-2([icon]="certificateIcon")
from-now([time]="status.certificateExpiration")
span.ml-3(title="Last Patreon update" [class.text-danger]="isOldPatreon")
fa-icon.mr-2([icon]="patreonIcon")
from-now([time]="status.lastPatreonUpdate")
pagination(
[totalItems]="filtered.length" [itemsPerPage]="itemsPerPage" [maxSize]="15" [(ngModel)]="currentPage"
@@ -12,32 +12,6 @@
button.btn.btn-primary(type="button" (click)="saveFields()") Save
button.btn.btn-default.ml-1(type="button" (click)="resetFields()") Reset
.col-6
.form
.form-group
h3 Patreon
.form-group.d-flex
button.btn.btn-default((click)="updatePatreon()")
| Update Data
.input-group.ml-1(style="width: 400px")
input.form-control(#patreonToken placeholder="patreon token")
.input-group-append
button.btn.btn-warning((click)="updatePatreonToken(patreonToken.value); patreonToken.value = ''")
| Update Token
.form-group
| To get new token, got to #[a(href="https://www.patreon.com/portal/registration/register-clients") My Clients],
| refresh token and update patreon token field with "Creator's Access Token" from the site.
.form-group
button.btn.btn-default((click)="getLastPatreonData()")
| Get last patreon data
.form
.form-group
h3 Other
.form-group
button.btn.btn-default((click)="updatePastSupporters()")
| Update past supporters
.row
.col-12
.form
@@ -85,17 +85,6 @@ ng-template(#alertModal)
//-span.badge.badge-admin.badge-primary([title]="account.birthdate | date:'yyyy-MM-dd'")
| {{age}}
span.badge.badge-admin.badge-supporter(
*ngIf="isPatreonOrSupporter"
[ngClass]="supporterClass"
[class.declined]="account.supporterDeclinedSince && supporterLevel"
[tooltip]="supporterTooltip")
fa-icon([icon]="supporterIcon" [fixedWidth]="true")
| {{supporterLevelString}}
span.badge.badge-admin.badge-supporter.badge-success(*ngIf="account.totalPledged" title="lifetime support")
| ${{account.totalPledged}}
span.badge.badge-admin.badge-danger(*ngIf="account.ignoresCount" title="ignored by")
| {{account.ignoresCount}}
@@ -24,23 +24,23 @@ const accountDuplicates = new Map<string, DuplicatesInfo>();
const predefinedAlerts = [
{
name: 'erp:flagged',
message: `Your account has been flagged for inappropriate bahavior on PG rated server. `
+ `Continuing that behavior may result in permanent ban.`
message: `Your account has been flagged for inappropriate behavior on a PG rated server. `
+ `Continuing that behavior may result in a permanent ban.`
},
{
name: 'erp:timeout',
message: `Your account has been timed out for inappropriate language and bahavior on PG rated server. `
+ `Continuing that behavior may result in permanent ban.`
message: `Your account has been timed out for inappropriate language and behavior on a PG rated server. `
+ `Continuing that behavior may result in a permanent ban.`
},
{
name: 'dups',
message: `Your account has been flagged for making multiple accounts. `
+ `Continuing that behavior may result in permanent ban.`
+ `Continuing that behavior may result in a permanent ban.`
},
{
name: 'under',
message: `Your account has been reported for being underage, please do NOT play on 18+ server. `
+ `Continuing that may result in permanent ban.`
message: `Your account has been reported for being underage, please do NOT play on an 18+ server. `
+ `Continuing that may result in a permanent ban.`
},
];
+3 -45
View File
@@ -2,55 +2,13 @@ h1(focusTitle) About
.row.text-large
.col-lg-7
p.lead.
A game of ponies building a town
p.lead A Pony Town custom server
p This game is a derivative work based on #[a(href="https://pony.town") Pony Town]. The developers of Pony Town are not responsible for it in any way.
h2 Contact
p For all inquiries please send an email to #[b {{contactEmail}}]
//- div
hr
h2.text-success Custom servers
p.
You can #[a.text-success(href="...") download the files] and start your own custom {{title}} server.
p.
The package contains server files and instruction on steps required to start your own server.
It also allows you to customize and modify the game to your liking.
p.
Feel free to use this to host a server for your friends, fandom, language group or just a general
server for everyone.
a.d-block.mx-auto.my-4.btn.btn-lg.btn-outline-success.px-3(href="..." style="max-width: 400px;")
| Download game files
p.
All necessary instructions required to build and run the server are included in README.md file
included in the package.
p.
Running the server requires basic knowledge of configuring and hosting web applications,
it's not intended for beginner users.
hr
div(*ngIf="patreonLink")
h2 Support {{title}}
p.
If you'd like to help keep {{title}} running, and support future development of the project,
please consider #[a([href]="patreonLink" target="_blank" rel="noopener noreferrer") supporting us on Patreon].
support-button.d-block.mx-auto.my-4(style="max-width: 400px;")
p.
Patreon supporter rewards include the following:
ul
li(*ngFor="let r of rewards")
| {{r}}
p.
Note that in order to receive any patreon rewards you need to connect your Patreon to your {{title}} account.
h2 Technology
p.
-35
View File
@@ -77,41 +77,6 @@ h1(focusTitle) Account settings
| Account removed successfully
.col-md-6
div(style="margin-bottom: 100px;")
.clearfix(*ngIf="showSupporter")
.form-group
h3
fa-icon.mr-2([icon]="starIcon" [ngClass]="supporterClass")
| Thank you for your support
supporter-pony.float-right(#pony (mouseenter)="pony.excite()" (mouseleave)="pony.reset()")
.form-group
p Your reward tier: #[b {{supporterTitle}}]
p Your rewards include:
ul
li(*ngFor="let r of supporterRewards")
| {{r}}
p(*ngIf="supporter > 0")
| Use <code>/ss hello</code> command to use supporter chat color.
p(*ngIf="supporter > 1")
| Use <code>/s1 hello</code> and <code>/s2 hello</code> commands to use lower tier supporter chat colors.
.clearfix(*ngIf="showSupporterInfo")
.form-group
h3 Supporter info
supporter-pony.float-right(#pony (mouseenter)="pony.excite()" (mouseleave)="pony.reset()")
.form-group.text-muted
p.
You've successfully connected your Patreon account to Pony Town.
If you support Pony Town on Patreon your supporter info will show up here.
p.
It should take no longer than 10 minutes to register your Patreon pledges.
div(style="margin-bottom: 100px;")
.form-group
.float-right
+1 -1
View File
@@ -35,7 +35,7 @@ export const routes: Routes = [
@NgModule({
imports: [
BrowserModule,
RouterModule,
RouterModule.forRoot(routes, { anchorScrolling: 'enabled' }),
FormsModule,
HttpClientModule,
PopoverModule.forRoot(),
+1 -3
View File
@@ -37,13 +37,11 @@
.text-right
.d-flex.flex-wrap
//- .text-nowrap.mr-2
| &copy; 2016-{{date | date:'yyyy'}}
| &copy; 2019-{{date | date:'yyyy'}}
| #[a.text-muted.mr-1([href]="twitterLink" target="_blank") {{copyright}}] |
.text-nowrap
a.text-muted.mr-2(*ngIf="twitterLink" [href]="twitterLink" target="_blank" title="Twitter")
fa-icon([icon]="twitterIcon" [fixedWidth]="true")
a.text-muted.mr-2(*ngIf="patreonLink" [href]="patreonLink" target="_blank" title="Patreon")
fa-icon([icon]="patreonIcon" [fixedWidth]="true")
a.text-muted.mr-2(*ngIf="contactEmail" href="mailto:{{contactEmail}}" target="_blank" title="Email")
fa-icon([icon]="emailIcon" [fixedWidth]="true")
div
+3
View File
@@ -119,18 +119,21 @@ $chat-pad: 5px;
position: fixed;
top: 5px;
right: 5px;
z-index: 2;
}
#friends-box {
position: fixed;
top: 5px;
right: 45px;
z-index: 2;
}
#swap-box {
position: fixed;
top: 5px;
right: 86px;
z-index: 2;
}
#notifications {
+3 -5
View File
@@ -8,8 +8,7 @@ import { GameService } from '../services/gameService';
import { Model, Friend } from '../services/model';
import { version, host, contactEmail, supporterLink, twitterLink, copyrightName } from '../../client/data';
import { PonyTownGame } from '../../client/game';
import { faTwitter, faPatreon, faEnvelope, faCog, faHome, faGamepad, faInfoCircle, faHorseHead } from '../../client/icons';
import { InstallService } from '../services/installService';
import { faTwitter, faPatreon, faEnvelope, faCog, faHome, faInfoCircle, faHorseHead, faQuestionCircle } from '../../client/icons';
import { OAuthProvider, Entity, FakeEntity, Pony } from '../../common/interfaces';
import { registerServiceWorker, isBrowserOutdated, checkIframeKey } from '../../client/clientUtils';
import { ErrorReporter } from '../services/errorReporter';
@@ -49,7 +48,7 @@ export class App implements OnInit, OnDestroy {
readonly patreonIcon = faPatreon;
readonly cogIcon = faCog;
readonly homeIcon = faHome;
readonly helpIcon = faGamepad;
readonly helpIcon = faQuestionCircle;
readonly aboutIcon = faInfoCircle;
readonly charactersIcon = faHorseHead;
readonly contactEmail = contactEmail;
@@ -67,12 +66,11 @@ export class App implements OnInit, OnDestroy {
private game: PonyTownGame,
private router: Router,
private activatedRoute: ActivatedRoute,
private installService: InstallService,
private errorReporter: ErrorReporter,
) {
}
get canInstall() {
return this.installService.canInstall;
return false;
}
get loading() {
return this.model.loading;
@@ -13,8 +13,7 @@ page-loader(*ngIf="!playing")
h1.sr-only(focusTitle) Characters
h2.sr-only Character selection
.mx-auto(style="max-width: 400px;")
support-button.mb-3
install-button.mb-3
visit-pt-button.mb-3
character-select(
[(error)]="error" [newButton]="true" [removeButton]="true" (change)="updateMuzzles()"
(preview)="previewPony = $event")
+9 -16
View File
@@ -5,7 +5,9 @@ h1(focusTitle) Help
section
h2 Contact
p For all inquiries please send an email to #[b {{contactEmail}}]
p.
For technical issues, see the #[a(routerLink="/help" fragment="issues") Common Issues] section. If it doesn't solve your problem,
send an email to #[b {{contactEmail}}].
section
h2 Controls
@@ -152,15 +154,6 @@ h1(focusTitle) Help
li <code>/removetoolbox</code> - removes toolbox from the house (need to be alone in the house or a party leader)
li <code>/restoretoolbox</code> - restores toolbox to the house (need to be alone in the house or a party leader)
section
h4 Supporter-only commands
ul.spaced-list
li <code>/ss</code> - supporter colored text
li <code>/s1</code> - tier 1 supporter colored text (for use by higher tier supporters)
li <code>/s2</code> - tier 2 supporter colored text (for use by higher tier supporters)
li <code>/s3</code> - tier 3 supporter colored text (for use by higher tier supporters)
section
h2 Chat emojis
p.
@@ -176,7 +169,7 @@ h1(focusTitle) Help
.col-md-5
section
h2 Common issues
h2#issues Common issues
section
h4 Keys stuck when using iPad keyboard
@@ -247,13 +240,13 @@ h1(focusTitle) Help
using #[b Chrome] or #[b Edge] browsers for best performance.
section
h4 Why can't I create multiple accounts
h4#rules Why can't I create multiple accounts
p.text-fading.
This restriction is in-place to protect you and the game itself from other players in the game.
This restriction is in place to protect you and the game itself from other players in the game.
p.text-fading.
Allowing multiple accounts for single person invalidates ignore and hide systems that
allow you to ignore and hide other players that are harassing or bothering you in any way.
It also invalidates mute and ban system that is in-place to protect you from players breaking
It also invalidates mute and ban system that is in place to protect you from players breaking
the game rules to bother or harass others.
p.text-fading.
Allowing use of multiple accounts at the same time would dramatically increase load on the
@@ -263,13 +256,13 @@ h1(focusTitle) Help
game anymore.
p.text-fading.
Use of multiple accounts also invalidates any limits (like character limit) that we put on the accounts.
The limits are in-place to prevent large amount of players that we have at the moment from
The limits are in place to prevent large amount of players that we have at the moment from
exhausting our limited server resources.
section
h4 Why can't I modify the game with hacks or scripts
p.text-fading.
This restriction is in-place to protect you and the game itself from other players in the game.
This restriction is in place to protect you and the game itself from other players in the game.
p.text-fading.
Almost all third-party scripts used in-game were used to harass other players. Even when the
creator of the script did not intend it to be used in that way, the limited knowledge of the
+12
View File
@@ -2,6 +2,7 @@ import { Component } from '@angular/core';
import { emojis } from '../../../client/emoji';
import { faArrowLeft, faArrowRight, faArrowUp, faArrowDown } from '../../../client/icons';
import { contactEmail } from '../../../client/data';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'help',
@@ -16,4 +17,15 @@ export class Help {
readonly emotes = emojis.map(e => e.names[0]);
readonly mac = /Macintosh/.test(navigator.userAgent);
readonly contactEmail = contactEmail;
// allow scrolling to #issues and #rules fragments
constructor(private route: ActivatedRoute) {}
ngAfterViewInit() {
this.route.fragment.subscribe(f => {
const element = document.querySelector("#" + f);
if (element) setTimeout(() => element.scrollIntoView(), 10);
})
}
}
+3 -3
View File
@@ -1,6 +1,6 @@
div(*ngIf="!playing")
.text-center.heading
img.pixelart.home-logo(revSrc="images/logo-large.png" alt="Pony Town")
img.pixelart.home-logo(revSrc="images/logo-large.png" alt="Pony Town Custom Server")
.mx-auto.home-content
page-loader
@@ -19,13 +19,13 @@ div(*ngIf="!playing")
div(*ngIf="!!account")
.form-group
support-button
visit-pt-button
.form-group(*ngIf="canInstall")
install-button
.form-group
character-select([(error)]="error" [editButton]="true" (preview)="previewPony = $event")
.form-group
character-preview([pony]="previewInfo" [state]="state" [passive]="true" [name]="previewName" [tag]="previewTag")
character-preview([pony]="previewInfo" [state]="state" [passive]="false" [name]="previewName" [tag]="previewTag")
.form-group.text-center
play-box([(error)]="error")
+21 -3
View File
@@ -2,8 +2,8 @@ import { Component } from '@angular/core';
import { Model, getPonyTag } from '../../services/model';
import { defaultPonyState } from '../../../client/ponyHelpers';
import { GameService } from '../../services/gameService';
import { InstallService } from '../../services/installService';
import { OAuthProvider, PonyObject } from '../../../common/interfaces';
import { stand } from '../../../client/ponyAnimations';
@Component({
selector: 'home',
@@ -14,10 +14,11 @@ export class Home {
state = defaultPonyState();
previewPony: PonyObject | undefined = undefined;
error?: string;
private animationTime = 0;
private interval?: any;
constructor(
private gameService: GameService,
private model: Model,
private installService: InstallService,
) {
}
get authError() {
@@ -27,7 +28,7 @@ export class Home {
return this.model.accountAlert;
}
get canInstall() {
return this.installService.canInstall;
return false;
}
get playing() {
return this.gameService.playing;
@@ -53,4 +54,21 @@ export class Home {
signIn(provider: OAuthProvider) {
this.model.signIn(provider);
}
ngOnInit() {
let last = Date.now();
this.interval = setInterval(() => {
const now = Date.now();
this.update((now - last) / 1000);
last = now;
}, 1000 / 24);
}
ngOnDestroy() {
clearInterval(this.interval);
}
update(delta: number) {
this.animationTime += delta;
const animation = stand;
this.state.animation = animation;
this.state.animationFrame = Math.floor(this.animationTime * animation.fps) % animation.frames.length;
}
}
+1 -1
View File
@@ -569,7 +569,7 @@ export class AdminModel {
const count = this.events.reduce((sum, e) => sum + (e.deleted ? 0 : 1), 0);
const inred = this.events.reduce((sum, e) => sum + ((!e.deleted && e.count > 9) ? 1 : 0), 0);
const flag = this.isLowDiskSpace || this.isLowMemory || this.isOldCertificate || this.isOldPatreon;
document.title = `${ponies} | ${count}${repeat('!', inred)}${flag ? ' 🚩' : ''}${!this.connected ? ' ⚠' : ''} | Pony Town`;
document.title = `${ponies} | ${count}${repeat('!', inred)}${flag ? ' 🚩' : ''}${!this.connected ? ' ⚠' : ''} | Your Town`;
}
private notify(title: string, body: string) {
if (this.notifications && notification.permission === 'granted') {
+1 -1
View File
@@ -20,7 +20,7 @@ export class InstallService {
}
}
get canInstall() {
return !!this.installEvent || (DEVELOPMENT && localStorage.getItem('install'));
return false;
}
install() {
if (!this.installEvent) {
+6 -2
View File
@@ -22,7 +22,7 @@ import { ErrorReporter } from './errorReporter';
import { randomString } from '../../common/stringUtils';
import { StorageService } from './storageService';
import { decompressPonyString, compressPonyString, decodePonyInfo } from '../../common/compressPony';
import { SECOND, PLAYER_DESC_MAX_LENGTH } from '../../common/constants';
import { SECOND, PLAYER_DESC_MAX_LENGTH, NEW_ACCOUNT_PONY_NAME } from '../../common/constants';
import { canUseTag } from '../../common/tags';
export interface Friend extends FriendData {
@@ -191,7 +191,11 @@ export class Model {
this.ponies = account.ponies ? account.ponies.sort(comparePonies) : [];
this.friends = undefined;
this.selectPony(getDefaultPony(this.ponies));
let defaultPony = getDefaultPony(this.ponies);
if (this.ponies.length === 0) {
defaultPony.name = NEW_ACCOUNT_PONY_NAME;
}
this.selectPony(defaultPony);
this.storage.setItem('vid', account.id);
this.loading = false;
this.accountAlert = account.alert;
@@ -1,3 +1,6 @@
ng-template(#actionsModal)
actions-modal((close)="closeActions()" [focusTrap]="true" (keydown)="$event.stopPropagation()")
.action-bar(
[class.has-scroller]="hasScroller" [class.is-mobile]="mobile" [class.is-blurred]="blurred"
#scroller (mousewheel)="scroll($event)" (wheel)="scroll($event)" (scroll)="true")
@@ -14,6 +17,8 @@
[draggablePad]="10"
(draggableDrag)="drag(i)"
(draggableDrop)="drop($event, i)")
button.action-settings.game-button((click)="openActions()" style="touch-action: inherit;")
fa-icon.action-icon([icon]="cogIcon" [fixedWidth]="true")
.action-button-padding
.scroller-label
| scroll using this bar
@@ -7,7 +7,7 @@
.action-bar {
overflow: hidden;
padding-bottom: 7px;
padding-bottom: 0px;
padding-top: 7px;
padding-right: 5px;
max-width: calc(100vw - 50px);
@@ -60,7 +60,14 @@
}
}
action-button {
.action-icon {
font-size: 30px;
position: relative;
right: 6px;
bottom: 8px;
}
action-button, .action-settings {
pointer-events: auto;
margin: 3px 6px;
@@ -1,4 +1,5 @@
import { Component, Input, ViewChild, ElementRef } from '@angular/core';
import { BsModalService, BsModalRef } from 'ngx-bootstrap/modal';
import { ButtonAction } from '../../../common/interfaces';
import { PonyTownGame } from '../../../client/game';
import { isMobile } from '../../../client/data';
@@ -6,6 +7,7 @@ import { useAction, serializeActions } from '../../../client/buttonActions';
import { SettingsService } from '../../services/settingsService';
import { ACTIONS_LIMIT } from '../../../common/constants';
import { last } from '../../../common/utils';
import { faCog } from '../../../client/icons';
@Component({
selector: 'action-bar',
@@ -14,11 +16,14 @@ import { last } from '../../../common/utils';
})
export class ActionBar {
@ViewChild('scroller', { static: true }) scroller!: ElementRef;
@ViewChild('actionsModal', { static: true }) actionsModal!: ElementRef;
@Input() blurred = false;
readonly cogIcon = faCog;
activeAction: ButtonAction | undefined = undefined;
shortcuts = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '='];
private modalRef?: BsModalRef;
private _editable = false;
constructor(private game: PonyTownGame, private settings: SettingsService) {
constructor(private game: PonyTownGame, private settings: SettingsService, private modalService: BsModalService) {
}
@Input() get editable() {
return this._editable;
@@ -68,6 +73,20 @@ export class ActionBar {
this.scroller.nativeElement.scrollLeft += delta * 20;
}
}
openActions() {
if (this.modalRef) {
this.closeActions();
}
else {
this.modalRef = this.modalService.show(this.actionsModal, { ignoreBackdropClick: true });
}
}
closeActions() {
if (this.modalRef) {
this.modalRef.hide();
this.modalRef = undefined;
}
}
private updateFreeSlots() {
const actions = this.actions;
@@ -11,6 +11,7 @@ import { element, textNode, removeAllNodes, replaceNodes } from '../../../client
import { DEFAULT_CHATLOG_OPACITY, PONY_TYPE } from '../../../common/constants';
import { faCaretUp, faArrowDown } from '../../../client/icons';
import { sampleMessages } from '../../../common/debugData';
import { isMobile } from '../../../client/data';
interface IndexEntryUser {
id: number;
@@ -354,7 +355,10 @@ export class ChatLog implements AfterViewInit, OnDestroy, DoCheck {
return (tab === 'local' || tab === 'party' || tab === 'whisper') ? tab : 'local';
}
get open() {
return !this.settings.chatlogClosed;
if (this.settings.chatlogClosed === undefined) {
return !isMobile; // closed by default on mobile
}
return this.settings.chatlogClosed === false;
}
get width() {
return this.settings.chatlogWidth || 500;
@@ -475,7 +479,7 @@ export class ChatLog implements AfterViewInit, OnDestroy, DoCheck {
}
}
toggle() {
this.settings.chatlogClosed = !this.settings.chatlogClosed;
this.settings.chatlogClosed = this.open;
this.settingsService.saveBrowserSettings();
this.updateOpen();
}
@@ -35,7 +35,6 @@ $color-picker-box-offset: 5px;
border: solid 1px $border-color;
border-radius: 2px;
pointer-events: none;
z-index: 10;
.disabled > & {
opacity: 0.8;
@@ -1,6 +1,6 @@
.btn-group.d-flex(*ngIf="canInstall")
button.btn.btn-lg.btn-outline-success.text-wrap.flex-grow-1((click)="install()")
| Add #[b Pony Town] to {{isMobile ? 'home screen' : 'desktop'}}
| Add #[b Pony Town Custom Server] to {{isMobile ? 'home screen' : 'desktop'}}
button.btn.btn-lg.btn-outline-success.flex-grow-0(
(click)="dismiss()" title="Dismiss" [attr.aria-label]="'Dismiss add to ' + (isMobile ? 'home screen' : 'desktop')")
fa-icon([icon]="closeIcon")
@@ -13,7 +13,7 @@ export class InstallButton {
constructor(private installService: InstallService) {
}
get canInstall() {
return this.installService.canInstall;
return false;
}
get isMobile() {
return isMobile;
@@ -1,8 +1,8 @@
nav.navbar.navbar-expand.justify-content-end(role="navigation")
.d-none.d-md-block
a.pixelart.logo(*ngIf="logo" routerLink="/" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }")
img.pixelart.logo-large(revSrc="images/logo-large.png" alt="Pony Town" width="287" height="65")
img.pixelart.logo-small(revSrc="images/logo-small.png" alt="Pony Town" width="37" height="43")
img.pixelart.logo-large(revSrc="images/logo-large.png" alt="Pony Town Custom Server" width="287" height="88")
img.pixelart.logo-small(revSrc="images/logo-small.png" alt="Pony Town Custom Server" width="37" height="43")
.navbar-collapse.text-right
.navbar-nav.ml-auto
@@ -18,7 +18,7 @@
.form-group(*ngIf="isAndroidBrowser")
.alert.alert-warning(role="alert")
| Your browser is outdated and is not able to correctly run Pony Town.
| Your browser is outdated and is not able to correctly run Pony Town Custom Server.
| Please install different browser to be able to play the game.
.form-group(*ngIf="model.missingBirthdate && !birthdateSet && requestBirthdate")
@@ -86,8 +86,8 @@
.form-group(*ngIf="isBrowserOutdated")
.alert.alert-warning(role="alert")
button.close.float-right((click)="dismissOutdatedBrowser()" aria-label="Close" style="font-size: 20px;") &times;
| Your browser is outdated and is known to have issues running Pony Town.
| Make sure you have latest version installed.
| Your browser is outdated and is known to have issues running Pony Town Custom Server.
| Make sure you have the latest version installed.
.form-group(*ngIf="invalidVersion && !offline")
.alert.alert-danger(role="alert")
@@ -2,9 +2,11 @@
h5 General rules
ul.text-muted.list-rules
li(*ngFor="let r of rules") {{r}}
p.text-muted For more details about the rules, see our #[a(routerLink="/help" fragment="rules") Help page].
hr
h5 Notice
h5 This is a Pony Town custom server
p.text-muted
| This game is very #[strong early in development]. There might be bugs and occasional downtimes.
| The developers of Pony Town are not responsible for this server or the contents on it in any way.
p.text-muted
| Please do not redistribute any of the game files or code.
| You can check out the original game #[a(href="https://pony.town") here].
@@ -12,32 +12,33 @@ ng-template(#actionsModal)
button.game-button.dropdown-toggle.no-arrow(dropdownToggle (click)="false" title="Settings")
fa-icon([icon]="cogIcon" [fixedWidth]="true")
.dropdown-menu.dropdown-menu-right.settings-box-menu(*dropdownMenu)
.dropdown-header.d-flex.justify-content-between
.dropdown-header.d-flex.justify-content-between.mb-1
span {{server}}
span.clock-display {{time}}
.dropdown-item.d-flex
.dropdown-item.d-flex.mb-1
fa-icon.mr-2([icon]="searchIcon" [fixedWidth]="true")
.flex-grow-1 Scale (x{{scale}})
button.btn.btn-xs.btn-outline-secondary((click)="zoomOut()" aria-label="Zoom out")
fa-icon([icon]="minusIcon" [fixedWidth]="true")
button.btn.btn-xs.btn-outline-secondary.ml-2((click)="zoomIn()" aria-label="Zoom in")
fa-icon([icon]="plusIcon" [fixedWidth]="true")
.dropdown-item.dropdown-item-static
.dropdown-item.dropdown-item-static.mb-1
a((click)="toggleVolume()" title="Toggle sound")
fa-icon([icon]="volumeIcon" [fixedWidth]="true")
//.flex-grow-1 Volume
slider-bar([(value)]="volume" [step]="1" (started)="volumeStarted()")
.text-muted.text-right(*ngIf="track" style="margin-top: -5px;")
small
| playing: #[b {{track}}]
a.text-muted.ml-1((click)="nextTrack()" title="Next track")
fa-icon([icon]="forwardIcon" [fixedWidth]="true")
a.dropdown-item((click)="unhideAllHiddenPlayers()")
a.dropdown-item((click)="unhideAllHiddenPlayers()").mb-1
fa-icon.mr-2([icon]="emptyIcon" [fixedWidth]="true")
| Unhide players (temporary)
a.dropdown-item((click)="openSettings()")
a.dropdown-item((click)="openSettings()").mb-1
fa-icon.mr-2([icon]="emptyIcon" [fixedWidth]="true")
| Settings
a.dropdown-item((click)="openActions()")
a.dropdown-item((click)="openActions()").mb-1
fa-icon.mr-2([icon]="emptyIcon" [fixedWidth]="true")
| Actions
//-a.dropdown-item((click)="openInvites()" *ngIf="hasInvites")
@@ -30,14 +30,12 @@
tab(title="Graphics" [icon]="graphicsIcon")
div(*tabContent)
.form-group
custom-checkbox(
*ngIf="!lockLowGraphicsMode" [(checked)]="browser.lowGraphicsMode"
help="Can improve game performance in some cases")
| Low graphics mode
custom-checkbox(
*ngIf="lockLowGraphicsMode" [checked]="true" [disabled]="true"
help="Can improve game performance in some cases")
| Low graphics mode
label#graphics-quality-label Graphics quality
div
slider-bar.mr-2.mb-2(
[(value)]="browser.graphicsQuality" [min]="0" [max]="maxGraphicsQuality" [step]="1"
style="max-width: 300px" labelledBy="graphics-quality-label")
| {{graphicsQualityText}}
.form-group
custom-checkbox(
[(checked)]="browser.brightNight"
@@ -1,6 +1,6 @@
import { Component, Output, EventEmitter, OnInit, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';
import { AccountSettings, BrowserSettings } from '../../../common/interfaces';
import { AccountSettings, BrowserSettings, GraphicsQuality } from '../../../common/interfaces';
import { SettingsService } from '../../services/settingsService';
import {
DEFAULT_CHATLOG_OPACITY, MAX_CHATLOG_RANGE, MIN_CHATLOG_RANGE, isChatlogRangeUnlimited, MAX_FILTER_WORDS_LENGTH
@@ -9,7 +9,7 @@ import { StorageService } from '../../services/storageService';
import { cloneDeep } from '../../../common/utils';
import { PonyTownGame } from '../../../client/game';
import { updateRangeIndicator, readFileAsText } from '../../../client/clientUtils';
import { faSlidersH, faCommentSlash, faGamepad, faImage, faDownload, faUpload } from '../../../client/icons';
import { faSlidersH, faCommentSlash, faGamepad, faImage, faDownload, faUpload, faComment } from '../../../client/icons';
@Component({
selector: 'settings-modal',
@@ -19,8 +19,10 @@ import { faSlidersH, faCommentSlash, faGamepad, faImage, faDownload, faUpload }
export class SettingsModal implements OnInit, OnDestroy {
readonly maxChatlogRange = MAX_CHATLOG_RANGE;
readonly minChatlogRange = MIN_CHATLOG_RANGE;
readonly maxGraphicsQuality: number;
readonly maxGraphicsQualityValue: GraphicsQuality;
readonly gameIcon = faSlidersH;
readonly chatIcon = faCommentSlash;
readonly chatIcon = faComment;
readonly filtersIcon = faCommentSlash;
readonly controlsIcon = faGamepad;
readonly graphicsIcon = faImage;
@@ -38,6 +40,24 @@ export class SettingsModal implements OnInit, OnDestroy {
private storage: StorageService,
private game: PonyTownGame,
) {
if (game.webgl) {
if (game.webgl.failedFBO) {
this.maxGraphicsQuality = 0;
this.maxGraphicsQualityValue = GraphicsQuality.Low;
}
else if (game.webgl.failedDepthBuffer) {
this.maxGraphicsQuality = 1;
this.maxGraphicsQualityValue = GraphicsQuality.Medium;
}
else {
this.maxGraphicsQuality = 2;
this.maxGraphicsQualityValue = GraphicsQuality.High;
}
}
else {
this.maxGraphicsQuality = 2;
this.maxGraphicsQualityValue = GraphicsQuality.High;
}
}
get pane() {
return this.storage.getItem('settings-modal-pane') || 'game';
@@ -45,13 +65,25 @@ export class SettingsModal implements OnInit, OnDestroy {
set pane(value: string) {
this.storage.setItem('settings-modal-pane', value);
}
get lockLowGraphicsMode() {
return this.game.failedFBO;
}
get chatlogRangeText() {
const range = this.account.chatlogRange;
return isChatlogRangeUnlimited(range) ? 'entire screen' : `${range} tiles`;
}
get graphicsQualityText() {
if (!this.game.webgl || (this.browser.graphicsQuality === undefined)) {
return 'Undefined';
}
if (this.game.webgl.failedFBO || (this.browser.graphicsQuality === GraphicsQuality.Low)) {
return 'Low';
}
else if (this.game.webgl.failedDepthBuffer || (this.browser.graphicsQuality === GraphicsQuality.Medium)) {
return 'Medium';
}
else {
return 'High';
}
}
ngOnInit() {
this.accountBackup = cloneDeep(this.settingsService.account);
this.browserBackup = cloneDeep(this.settingsService.browser);
@@ -120,6 +152,10 @@ export class SettingsModal implements OnInit, OnDestroy {
if (this.account.filterWords === undefined) {
this.account.filterWords = '';
}
if (this.browser.graphicsQuality === undefined) {
this.browser.graphicsQuality = this.maxGraphicsQualityValue;
}
}
export() {
const account = { ...this.account, actions: undefined };
+4 -4
View File
@@ -23,8 +23,6 @@ import { EmoteBox } from './emote-box/emote-box';
import { SliderBar } from './slider-bar/slider-bar';
import { SpriteBox } from './sprite-box/sprite-box';
import { SpriteSelection } from './sprite-selection/sprite-selection';
import { SupportButton } from './support-button/support-button';
import { SupporterPony } from './supporter-pony/supporter-pony';
import { SetSelection, SetOutlineHidden } from './set-selection/set-selection';
import { CheckBox } from './check-box/check-box';
import { PortraitBox } from './portrait-box/portrait-box';
@@ -73,6 +71,8 @@ import { dropdownDirectives } from './directives/dropdown';
import { SaveActiveTab } from './directives/saveActiveTab';
import { SiteNamePipe } from './pipes/siteName';
import { VisitPTButton } from './visit-pt-button/visit-pt-button';
import { VisitPTPony } from './visit-pt-pony/visit-pt-pony';
const declarations = [
ActionBar,
@@ -89,8 +89,8 @@ const declarations = [
SliderBar,
SpriteBox,
SpriteSelection,
SupportButton,
SupporterPony,
VisitPTButton,
VisitPTPony,
SetSelection,
SetOutlineHidden,
CheckBox,
@@ -1,10 +0,0 @@
a.btn.btn-lg.btn-outline-patreon.btn-block.mb-2(
*ngIf="patreonLink" (mouseenter)="pony.excite()" (mouseout)="pony.reset()" [href]="patreonLink" target="_blank"
rel="noopener noreferrer")
| {{supporter ? 'Thank you for supporting us!' : 'Support us on Patreon!'}}
svg.patreon-logo(xmlns="http://www.w3.org/2000/svg" viewBox="0 0 150 150")
circle(fill="#f9685f" cx="75" cy="75" r="75")
path(fill="#052d49" d="M31.729 27.659h17.578v95.888H31.729z")
circle(fill="#fff" cy="63.565" cx="95.783" r="35.955")
.supporter-pony
supporter-pony(#pony [scale]="2")
@@ -1,17 +0,0 @@
import { Component } from '@angular/core';
import { Model } from '../../services/model';
import { supporterLink } from '../../../client/data';
@Component({
selector: 'support-button',
templateUrl: 'support-button.pug',
styleUrls: ['support-button.scss'],
})
export class SupportButton {
readonly patreonLink = supporterLink;
constructor(private model: Model) {
}
get supporter() {
return this.model.supporter;
}
}
@@ -0,0 +1,8 @@
a.btn.btn-lg.btn-pt.btn-outline-primary.btn-block.mb-2(
*ngIf="enableVisitPTButton" (mouseenter)="pony.select()" (mouseout)="pony.reset()" [href]="ptLink" target="_blank"
rel="noopener noreferrer")
.pt-logo
img.pixelart.logo(revSrc="images/logo-small.png")
| {{'Visit Pony Town main server!'}}
.visit-pt-pony
visit-pt-pony(#pony [scale]="2")
@@ -12,30 +12,36 @@
}
}
.patreon-logo {
.btn-pt {
padding: 1.0rem;
font-weight: 500;
//height: 60px;
}
.pt-logo {
position: absolute;
left: 8px;
top: 5px;
width: 30px;
height: 30px;
top: 6px;
width: 37px;
height: 47px;
@media (max-width: 320px) {
display: none;
}
}
.supporter-pony {
.visit-pt-pony {
position: absolute;
right: 0;
bottom: 0;
height: 70px;
height: 80px;
width: 100px;
overflow: hidden;
border-radius: $border-radius-lg - 2px;
> supporter-pony {
> visit-pt-pony {
position: absolute;
bottom: -95px;
bottom: -80px;
left: -20px;
}
@@ -0,0 +1,13 @@
import { Component } from '@angular/core';
@Component({
selector: 'visit-pt-button',
templateUrl: 'visit-pt-button.pug',
styleUrls: ['visit-pt-button.scss'],
})
export class VisitPTButton {
readonly ptLink = 'http://pony.town';
readonly enableVisitPTButton = true;
constructor() {
}
}
@@ -1,41 +1,42 @@
import { Component, OnInit, OnDestroy, Input, ViewChild } from '@angular/core';
import { defaultExpression } from '../../../client/ponyUtils';
import { defaultPonyState } from '../../../client/ponyHelpers';
import { SUPPORTER_PONY } from '../../../common/constants';
import { Expression, Muzzle, HeadAnimation, Iris } from '../../../common/interfaces';
import { excite } from '../../../client/ponyAnimations';
import { Expression, Muzzle, HeadAnimation, BodyAnimation, Eye } from '../../../common/interfaces';
import { excite_meno, happy_tongue_meno, stand, boop, happy_tongue_meno_2 } from '../../../client/ponyAnimations';
import { FrameService, FrameLoop } from '../../services/frameService';
import { CharacterPreview } from '../character-preview/character-preview';
import { decompressPonyString } from '../../../common/compressPony';
const BLEP: Expression = {
...defaultExpression,
left: Eye.Neutral2,
right: Eye.Neutral2,
muzzle: Muzzle.Blep,
};
const EXCITED: Expression = {
...defaultExpression,
left: Eye.Neutral2,
right: Eye.Neutral2,
muzzle: Muzzle.SmileOpen,
};
const DERP: Expression = {
...defaultExpression,
muzzle: Muzzle.SmileOpen,
leftIris: Iris.Up,
};
const MENO = 'DBWIzP8imd08//D19fVazSjcwf1GhLNEiMxENSovLy/NsIdJQDnGqYBiSztWQTM6LychEnE/KRX///9WPeE1HbSpBUEIIASwgEIAAAAAbIAgCFMY34AHAAjwgCMRQCVLY38IDhAAwmFBz4fAIxjKM6SjGlaxjWtYgjGI';
@Component({
selector: 'supporter-pony',
templateUrl: 'supporter-pony.pug',
selector: 'visit-pt-pony',
templateUrl: 'visit-pt-pony.pug',
})
export class SupporterPony implements OnInit, OnDestroy {
export class VisitPTPony implements OnInit, OnDestroy {
@ViewChild('characterPreview', { static: true }) characterPreview!: CharacterPreview;
@Input() scale = 3;
pony = decompressPonyString(SUPPORTER_PONY);
pony = decompressPonyString(MENO);
state = defaultPonyState();
private expression?: Expression;
private headAnimation?: HeadAnimation;
private headTime = 0;
private bodyAnimation?: BodyAnimation;
private bodyTime = 0;
private loop: FrameLoop;
constructor(frameService: FrameService) {
this.loop = frameService.create(delta => this.tick(delta));
@@ -46,16 +47,29 @@ export class SupporterPony implements OnInit, OnDestroy {
ngOnDestroy() {
this.loop.destroy();
}
excite() {
select() {
this.headTime = 0;
this.headAnimation = excite;
this.expression = Math.random() < 0.2 ? DERP : EXCITED;
this.bodyTime = 0;
this.expression = EXCITED;
this.bodyAnimation = Math.random() < 0.25 ? boop : stand;
const headRandom = Math.random();
if (headRandom < 0.5) {
this.headAnimation = excite_meno;
}
else if (headRandom < 0.8) {
this.headAnimation = happy_tongue_meno;
}
else {
this.headAnimation = happy_tongue_meno_2;
}
}
reset() {
this.expression = undefined;
}
private tick(delta: number) {
this.headTime += delta;
this.bodyTime += delta;
if (this.headAnimation) {
const frame = Math.floor(this.headTime * this.headAnimation.fps);
@@ -83,6 +97,19 @@ export class SupporterPony implements OnInit, OnDestroy {
}
}
if (this.bodyAnimation) {
const frame = Math.floor(this.bodyTime * this.bodyAnimation.fps * 0.75);
if (frame >= this.bodyAnimation.frames.length && !this.bodyAnimation.loop) {
this.bodyAnimation = undefined;
this.state.animation = stand;
this.state.animationFrame = 0;
} else {
this.state.animation = this.bodyAnimation;
this.state.animationFrame = frame % this.bodyAnimation.frames.length;
}
}
this.state.expression = this.expression;
}
}
@@ -113,7 +113,6 @@ export class ToolsUI implements OnInit, OnDestroy {
],
};
game.onClock.next('00:00');
game.failedFBO = true;
game.send = <T>(action: (server: any) => T) => action({
action() { },
select() { },
+1 -1
View File
@@ -1 +1 @@
{"hash":"Ry9ADLb3uy","stamp":164133449}
{"hash":"NBZLdEe0Jv","stamp":3244405878}
+2 -2
View File
@@ -1,2 +1,2 @@
export const HASH = 'Ry9ADLb3uy';
export const STAMP = 164133449;
export const HASH = 'NBZLdEe0Jv';
export const STAMP = 3244405878;
+3 -3
View File
@@ -1,5 +1,5 @@
export const REV: { [key: string]: string; } = {
'images/avatars.jpg': '0f8cf8266f',
'images/avatars.jpg': 'db381b64ec',
'images/logo-120.png': 'b051cf1915',
'images/logo-64.png': '64a63307c9',
'images/logo-gray.png': '7d4473f069',
@@ -7,8 +7,8 @@ export const REV: { [key: string]: string; } = {
'images/logo-small.png': '770b141d9d',
'images/logo.png': 'fedfb401e4',
'images/pony.png': '541b24abea',
'images/pony2.png': 'f370ad056d',
'images/pony2a.png': 'a6292b4e18',
'images/pony2.png': 'd949ad66f9',
'images/pony2a.png': 'efa4df6ddb',
'music/ambient.mp3': 'd37b620d70',
'music/ambient.webm': '4dbe61d738',
'music/bossanova.mp3': 'd43e480500',
+4 -12
View File
@@ -1,17 +1,9 @@
/* tslint:disable */
export const basicShader = `// VERTEX attribute vec2 position; attribute vec2 texcoords; uniform mat4 transform; varying vec2 textureCoord; void main() { textureCoord = texcoords; gl_Position = transform * vec4(position, 0, 1); } // FRAGMENT precision mediump float; uniform sampler2D sampler1; varying vec2 textureCoord; void main() { gl_FragColor = texture2D(sampler1, textureCoord); }`;
export const lightShader = `// VERTEX attribute vec2 position; attribute vec2 texcoords; attribute vec4 vertexColor; uniform mat4 transform; uniform vec4 lighting; varying vec2 textureCoord; varying vec4 vColor; void main() { textureCoord = texcoords; vColor = vertexColor * lighting; gl_Position = transform * vec4(position, 0, 1); } // FRAGMENT precision mediump float; varying vec2 textureCoord; varying vec4 vColor; void main() { float d = clamp(1.1 - length(textureCoord), 0.0, 1.0); float m = d * d; // * d; gl_FragColor = vec4(m, m, m, 1) * vColor; }`;
export const lightShader = `// VERTEX attribute vec2 position; attribute vec2 texcoords; attribute vec4 vertexColor; uniform mat4 transform; uniform vec4 lighting; varying vec2 textureCoord; varying vec4 vColor; void main() { textureCoord = texcoords; vColor = vertexColor * lighting; gl_Position = transform * vec4(position, 0, 1); } // FRAGMENT precision mediump float; varying vec2 textureCoord; varying vec4 vColor; void main() { float d = clamp((1.0 - length(textureCoord)) + 0.1, 0.0, 1.0); float m = d * d * d; gl_FragColor = vec4(m, m, m, 1) * vColor; }`;
export const mergeShader = `// VERTEX attribute vec2 position; attribute vec2 texcoords; uniform mat4 transform; uniform vec2 textureSize; varying vec2 textureCoord; void main() { textureCoord = texcoords / textureSize; gl_Position = transform * vec4(position, 0, 1); } // FRAGMENT precision mediump float; uniform sampler2D sampler1; uniform sampler2D sampler2; varying vec2 textureCoord; void main() { vec3 color1 = texture2D(sampler1, textureCoord).rgb; vec3 color2 = texture2D(sampler2, textureCoord).rgb; gl_FragColor = vec4(color1 * color2, 1.0); }`;
export const paletteDepthShader = `// VERTEX attribute vec3 position; attribute vec4 texcoords; attribute vec4 vertexColor; uniform mat4 transform; uniform vec4 lighting; varying vec4 textureCoord; varying vec4 vColor; void main() { textureCoord = texcoords; vColor = vertexColor * lighting; gl_Position = transform * vec4(position, 1); } // FRAGMENT precision mediump float; uniform sampler2D sampler1; // sprite uniform sampler2D sampler2; // palette uniform float pixelSize; varying vec4 textureCoord; varying vec4 vColor; void main() { vec4 sprite = texture2D(sampler1, vec2(textureCoord.x, textureCoord.y)); vec4 palette = texture2D(sampler2, vec2(textureCoord.z + sprite.x * pixelSize, textureCoord.w)); vec4 color = vec4(palette.xyz * sprite.y, palette.w) * vColor; gl_FragColor = color; if (color.a < 0.01) discard; }`;
export const paletteLayersShader = `// VERTEX attribute vec3 position; attribute vec4 texcoords; attribute vec4 vertexColor; attribute vec4 vertexColor1; uniform mat4 transform; uniform vec4 lighting; uniform float textureSize; varying vec4 textureCoord; varying vec4 vColor; varying vec4 vColor1; void main() { textureCoord = vec4(texcoords.xy * textureSize, texcoords.zw); // float f = texcoords.z; // float fr = fract(texcoords.z); // textureCoord.z = fr; // textureCoord.w = (f - fr) / 1024.0; vColor = vertexColor * lighting; vColor1 = vertexColor1; float depth = position.z; #ifdef DEPTH_BUFFERED if (vColor.a < 0.975) { depth = 0.0; } #endif gl_Position = transform * vec4(position.xy, depth, 1); } // FRAGMENT precision mediump float; uniform sampler2D sampler1; // sprite uniform sampler2D sampler2; // palette uniform float pixelSize; varying vec4 textureCoord; varying vec4 vColor; varying vec4 vColor1; void main() { vec4 sprite = texture2D(sampler1, textureCoord.xy); float shade = clamp(sprite.g + vColor1.a, 0.0, 1.0); vec4 mask = vec4(vColor1.rgb, 1.0 - (vColor1.r + vColor1.g + vColor1.b)); float paletteIndex = dot(mask, sprite); vec2 paletteCoord = textureCoord.zw; vec4 palette = texture2D(sampler2, vec2(paletteCoord.x + paletteIndex * pixelSize, paletteCoord.y)); gl_FragColor = vec4(palette.xyz * shade, palette.w) * vColor; #ifdef DEPTH_BUFFERED if (gl_FragColor.a < 0.01) { discard; } #endif }`;
export const paletteLayersInstancedShader = `// VERTEX attribute vec2 position0; attribute vec4 position1; attribute vec4 texcoord0; attribute vec2 texcoord1; attribute vec4 vertexColor; attribute vec4 vertexColor1; uniform mat4 transform; uniform vec4 lighting; varying vec2 textureCoord0; varying vec2 textureCoord1; varying vec4 vColor; varying vec4 vColor1; void main() { textureCoord0 = vec2(texcoord0.x + position0.x * texcoord0.z, texcoord0.y + position0.y * texcoord0.w); textureCoord1 = texcoord1; vColor = vertexColor * lighting; vColor1 = vertexColor1; gl_Position = transform * vec4( position1.x + position0.x * position1.z, position1.y + position0.y * position1.w, 0, 1); } // FRAGMENT precision mediump float; uniform sampler2D sampler1; // sprite uniform sampler2D sampler2; // palette uniform float pixelSize; varying vec2 textureCoord0; varying vec2 textureCoord1; varying vec4 vColor; varying vec4 vColor1; void main() { vec4 sprite = texture2D(sampler1, textureCoord0.xy); float shade = clamp(sprite.g + vColor1.a, 0.0, 1.0); vec4 mask = vec4(vColor1.rgb, 1.0 - (vColor1.r + vColor1.g + vColor1.b)); float paletteIndex = dot(mask, sprite); vec4 palette = texture2D(sampler2, vec2(textureCoord1.x + paletteIndex * pixelSize, textureCoord1.y)); gl_FragColor = vec4(palette.xyz * shade, palette.w) * vColor; }`;
export const paletteLayersShader = `// VERTEX attribute vec2 position; attribute vec4 texcoords; attribute vec4 vertexColor; attribute vec4 vertexColor1; uniform mat4 transform; uniform vec4 lighting; varying vec4 textureCoord; varying vec4 vColor; varying vec4 vColor1; void main() { textureCoord = texcoords; // float f = texcoords.z; // float fr = fract(texcoords.z); // textureCoord.z = fr; // textureCoord.w = (f - fr) / 1024.0; vColor = vertexColor * lighting; vColor1 = vertexColor1; gl_Position = transform * vec4(position, 0, 1); } // FRAGMENT precision mediump float; uniform sampler2D sampler1; // sprite uniform sampler2D sampler2; // palette uniform float pixelSize; uniform float textureSize; varying vec4 textureCoord; varying vec4 vColor; varying vec4 vColor1; void main() { vec4 sprite = texture2D(sampler1, textureCoord.xy / textureSize); float shade = clamp(sprite.g + vColor1.a, 0.0, 1.0); vec4 mask = vec4(vColor1.rgb, 1.0 - (vColor1.r + vColor1.g + vColor1.b)); float paletteIndex = dot(mask, sprite); vec2 paletteCoord = textureCoord.zw; vec4 palette = texture2D(sampler2, vec2(paletteCoord.x + paletteIndex * pixelSize, paletteCoord.y)); gl_FragColor = vec4(palette.xyz * shade, palette.w) * vColor; }`;
export const paletteShader = `// VERTEX attribute vec2 position; attribute vec4 texcoords; attribute vec4 vertexColor; uniform mat4 transform; uniform vec4 lighting; varying vec4 textureCoord; varying vec4 vColor; void main() { textureCoord = texcoords; vColor = vertexColor * lighting; gl_Position = transform * vec4(position, 0, 1); } // FRAGMENT precision mediump float; uniform sampler2D sampler1; // sprite uniform sampler2D sampler2; // palette uniform float pixelSize; varying vec4 textureCoord; varying vec4 vColor; void main() { vec4 sprite = texture2D(sampler1, vec2(textureCoord.x, textureCoord.y)); vec4 palette = texture2D(sampler2, vec2(textureCoord.z + sprite.x * pixelSize, textureCoord.w)); gl_FragColor = vec4(palette.xyz * sprite.y, palette.w) * vColor; }`;
export const spriteShader = `// VERTEX attribute vec2 position; attribute vec2 texcoords; attribute vec4 vertexColor; uniform mat4 transform; uniform vec4 lighting; varying vec2 textureCoord; varying vec4 vColor; void main() { textureCoord = texcoords; vColor = vertexColor * lighting; gl_Position = transform * vec4(position, 0, 1); } // FRAGMENT precision mediump float; uniform sampler2D sampler1; uniform float textureSize; varying vec2 textureCoord; varying vec4 vColor; void main() { gl_FragColor = texture2D(sampler1, textureCoord / textureSize) * vColor; }`;
export const sprite2Shader = `// VERTEX attribute vec3 position; attribute vec2 texcoords; attribute vec4 vertexColor; uniform mat4 transform; varying vec2 textureCoord; varying vec4 vColor; void main() { textureCoord = texcoords; vColor = vertexColor; gl_Position = transform * vec4(position, 1); } // FRAGMENT precision mediump float; uniform sampler2D sampler1; varying vec2 textureCoord; varying vec4 vColor; void main() { gl_FragColor = texture2D(sampler1, textureCoord) * vColor; }`;
export const spriteShader = `// VERTEX attribute vec3 position; attribute vec2 texcoords; attribute vec4 vertexColor; uniform mat4 transform; uniform vec4 lighting; uniform vec2 textureSize; varying vec2 textureCoord; varying vec4 vColor; void main() { textureCoord = texcoords / textureSize; vColor = vertexColor * lighting; gl_Position = transform * vec4(position, 1); } // FRAGMENT precision mediump float; uniform sampler2D sampler1; varying vec2 textureCoord; varying vec4 vColor; void main() { gl_FragColor = texture2D(sampler1, textureCoord); #ifdef USE_COLOR gl_FragColor *= vColor; #endif }`;
+2257 -2282
View File
File diff suppressed because one or more lines are too long
+28 -24
View File
@@ -1,4 +1,4 @@
import { Sprite, Batch, SpriteBatchBase, SpriteSheet } from '../common/interfaces';
import { Sprite, Batch, SpriteBatchBase } from '../common/interfaces';
import { WHITE } from '../common/colors';
import { colorToFloat, colorToFloatAlpha } from '../common/color';
import { BaseStateBatch } from './baseStateBatch';
@@ -38,36 +38,45 @@ export function getColorFloat(color: number, alpha: number) {
}
export abstract class BaseSpriteBatch extends BaseStateBatch implements SpriteBatchBase {
tris = 0;
depth = 1;
drawnTrisStats = 0;
flushes = 0;
index = 0;
spritesCount = 0;
vertices!: Float32Array;
verticesUint32!: Uint32Array;
vao: VAO | undefined = undefined;
rectSprite: Sprite | undefined = undefined;
vertexBuffer: WebGLBuffer | undefined = undefined;
indexBuffer: WebGLBuffer | undefined = undefined;
spriteSheet: SpriteSheet | undefined = undefined;
floatsPerSprite: number;
batching = false;
vao: VAO | undefined;
vertexBuffer: WebGLBuffer | undefined;
protected indexBuffer: WebGLBuffer;
protected vertices!: Float32Array;
protected spritesCapacity: number;
private floatsPerSprite: number;
private batching = false;
private startBatchIndex = 0;
private startBatchSprites = 0;
constructor(
public gl: WebGLRenderingContext,
public capacity: number,
buffer: ArrayBuffer,
vertexBuffer: WebGLBuffer,
public vertexCapacityMax: number,
indexBuffer: WebGLBuffer,
public attributes: VAOAttributeDefinition[],
) {
super();
this.floatsPerSprite = getVAOAttributesSize(gl, attributes);
this.vertices = new Float32Array(buffer, 0, capacity * this.floatsPerSprite);
this.verticesUint32 = new Uint32Array(buffer, 0, capacity * this.floatsPerSprite);
const bytesPerVertex = getVAOAttributesSize(gl, attributes);
this.vertices = new Float32Array(vertexCapacityMax * bytesPerVertex);
this.spritesCapacity = (vertexCapacityMax / 4) | 0; // 4 vertices per sprite
this.floatsPerSprite = bytesPerVertex | 0; // bytesPerVertex * 4 / sizeof(float)
const vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
throw new Error(`Failed to allocate vertex buffer`);
}
this.vertexBuffer = vertexBuffer;
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW);
this.indexBuffer = indexBuffer;
this.vao = createVAO(gl, createVAOAttributes(gl, attributes, vertexBuffer), indexBuffer);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
}
dispose() {
disposeBuffers(this.gl, this);
@@ -95,14 +104,13 @@ export abstract class BaseSpriteBatch extends BaseStateBatch implements SpriteBa
const batchSpriteCount = (batch.length / this.floatsPerSprite) | 0;
if (this.capacity < (this.spritesCount + batchSpriteCount)) {
if (this.spritesCapacity < (this.spritesCount + batchSpriteCount)) {
this.flush();
}
this.vertices.set(batch, this.index);
this.index += batch.length;
this.spritesCount += batchSpriteCount;
this.tris += batchSpriteCount * 2;
}
startBatch() {
if (this.batching) {
@@ -156,6 +164,7 @@ export abstract class BaseSpriteBatch extends BaseStateBatch implements SpriteBa
this.vao.draw(this.gl.TRIANGLES, this.startBatchSprites * 6, 0);
TIMING && timeEnd();
this.drawnTrisStats += this.startBatchSprites * 2;
this.spritesCount -= this.startBatchSprites;
this.index -= this.startBatchIndex;
this.vertices.copyWithin(0, this.startBatchIndex, this.startBatchIndex + this.index);
@@ -171,6 +180,7 @@ export abstract class BaseSpriteBatch extends BaseStateBatch implements SpriteBa
this.vao.draw(this.gl.TRIANGLES, this.spritesCount * 6, 0);
TIMING && timeEnd();
this.drawnTrisStats += this.spritesCount * 2;
this.spritesCount = 0;
this.index = 0;
}
@@ -184,19 +194,13 @@ function disposeBuffers(gl: WebGLRenderingContext, batch: BaseSpriteBatch) {
if (batch.vao) {
batch.vao.dispose();
}
if (batch.vertexBuffer) {
gl.deleteBuffer(batch.vertexBuffer);
}
if (batch.indexBuffer) {
gl.deleteBuffer(batch.indexBuffer);
}
} catch (e) {
DEVELOPMENT && console.error(e);
}
batch.vao = undefined;
batch.vertexBuffer = undefined;
batch.indexBuffer = undefined;
}
+3
View File
@@ -23,6 +23,7 @@ export function drawCanvas(
}
export class ContextSpriteBatch extends BaseStateBatch implements PaletteSpriteBatch, SpriteBatch {
depth = 1;
pixelSize = 1;
disableShading = false;
ignoreColor = 0;
@@ -137,6 +138,8 @@ export class ContextSpriteBatch extends BaseStateBatch implements PaletteSpriteB
}
releaseBatch() {
}
flush() {
}
}
const min = Math.min;
+1 -1
View File
@@ -158,7 +158,7 @@ export class PaletteManager implements IPaletteManager {
private initializeTexture(gl: WebGLRenderingContext, size: number) {
try {
if (!this.paletteTexture) {
this.paletteTexture = createEmptyTexture(gl, size, size, gl.RGBA, gl.UNSIGNED_BYTE);
this.paletteTexture = createEmptyTexture(gl, true, size, size, gl.RGBA, gl.UNSIGNED_BYTE);
} else if (this.paletteTexture.width !== size) {
resizeTexture(gl, this.paletteTexture, size, size);
}
+56 -59
View File
@@ -59,10 +59,12 @@ function pushQuad(
*/
function pushQuad(
vertices: Float32Array, //_verticesUint32: Uint32Array,
vertices: Float32Array,
transform: Matrix2D, index: number, type: number, color: number, palette: Palette,
sx: number, sy: number, sw: number, sh: number, dx: number, dy: number, dw: number, dh: number,
sx: number, sy: number, sw: number, sh: number, dx: number, dy: number, dz: number, dw: number, dh: number,
) {
index |= 0;
const c1 = types[type];
const y2 = dy + dh;
@@ -83,63 +85,62 @@ function pushQuad(
const t4 = transform[4];
const t5 = transform[5];
// pushVertex(vertices, index, dx, dy, u1, v1, pu, pv, color, c1, transform);
vertices[(index + 0) | 0] = t0 * dx + t2 * dy + t4;
vertices[(index + 1) | 0] = t1 * dx + t3 * dy + t5;
vertices[(index + 2) | 0] = u1;
vertices[(index + 3) | 0] = v1;
vertices[(index + 4) | 0] = pu;
vertices[(index + 5) | 0] = pv;
vertices[(index + 6) | 0] = color;
vertices[(index + 7) | 0] = c1;
vertices[index++] = t0 * dx + t2 * dy + t4;
vertices[index++] = t1 * dx + t3 * dy + t5;
vertices[index++] = dz;
vertices[index++] = u1;
vertices[index++] = v1;
vertices[index++] = pu;
vertices[index++] = pv;
vertices[index++] = color;
vertices[index++] = c1;
// pushVertex(vertices, index + 8, x2, dy, u2, v1, pu, pv, color, c1, transform);
vertices[(index + 8) | 0] = t0 * x2 + t2 * dy + t4;
vertices[(index + 9) | 0] = t1 * x2 + t3 * dy + t5;
vertices[(index + 10) | 0] = u2;
vertices[(index + 11) | 0] = v1;
vertices[(index + 12) | 0] = pu;
vertices[(index + 13) | 0] = pv;
vertices[(index + 14) | 0] = color;
vertices[(index + 15) | 0] = c1;
vertices[index++] = t0 * x2 + t2 * dy + t4;
vertices[index++] = t1 * x2 + t3 * dy + t5;
vertices[index++] = dz;
vertices[index++] = u2;
vertices[index++] = v1;
vertices[index++] = pu;
vertices[index++] = pv;
vertices[index++] = color;
vertices[index++] = c1;
// pushVertex(vertices, index + 16, x2, y2, u2, v2, pu, pv, color, c1, transform);
vertices[(index + 16) | 0] = t0 * x2 + t2 * y2 + t4;
vertices[(index + 17) | 0] = t1 * x2 + t3 * y2 + t5;
vertices[(index + 18) | 0] = u2;
vertices[(index + 19) | 0] = v2;
vertices[(index + 20) | 0] = pu;
vertices[(index + 21) | 0] = pv;
vertices[(index + 22) | 0] = color;
vertices[(index + 23) | 0] = c1;
vertices[index++] = t0 * x2 + t2 * y2 + t4;
vertices[index++] = t1 * x2 + t3 * y2 + t5;
vertices[index++] = dz;
vertices[index++] = u2;
vertices[index++] = v2;
vertices[index++] = pu;
vertices[index++] = pv;
vertices[index++] = color;
vertices[index++] = c1;
// pushVertex(vertices, index + 24, dx, y2, u1, v2, pu, pv, color, c1, transform);
vertices[(index + 24) | 0] = t0 * dx + t2 * y2 + t4;
vertices[(index + 25) | 0] = t1 * dx + t3 * y2 + t5;
vertices[(index + 26) | 0] = u1;
vertices[(index + 27) | 0] = v2;
vertices[(index + 28) | 0] = pu;
vertices[(index + 29) | 0] = pv;
vertices[(index + 30) | 0] = color;
vertices[(index + 31) | 0] = c1;
vertices[index++] = t0 * dx + t2 * y2 + t4;
vertices[index++] = t1 * dx + t3 * y2 + t5;
vertices[index++] = dz;
vertices[index++] = u1;
vertices[index++] = v2;
vertices[index++] = pu;
vertices[index++] = pv;
vertices[index++] = color;
vertices[index++] = c1;
return index + 32;
return index;
}
// function colorWithAlpha(color: number, alpha: number) {
// return ((color & 0xffffff00) | (((color & 0xff) * alpha) & 0xff)) >>> 0;
// }
export const PALETTE_BATCH_BYTES_PER_VERTEX = 2 * 4 + 4 * 4 + 4 + 4;
export class PaletteSpriteBatch extends BaseSpriteBatch implements IPaletteSpriteBatch {
depth = 1;
palette = true;
defaultPalette: Palette = createPalette(new Uint32Array(0));
constructor(
gl: WebGLRenderingContext, capacity: number, buffer: ArrayBuffer, vertexBuffer: WebGLBuffer, indexBuffer: WebGLBuffer
gl: WebGLRenderingContext, vertexCapacityMax: number, indexBuffer: WebGLBuffer
) {
super(gl, capacity, buffer, vertexBuffer, indexBuffer, [
{ name: 'position', size: 2 },
super(gl, vertexCapacityMax, indexBuffer, [
{ name: 'position', size: 3 },
{ name: 'texcoord0', size: 4 }, //, type: gl.UNSIGNED_SHORT },
{ name: 'color', size: 4, type: gl.UNSIGNED_BYTE, normalized: true },
{ name: 'color1', size: 4, type: gl.UNSIGNED_BYTE, normalized: true },
@@ -149,37 +150,35 @@ export class PaletteSpriteBatch extends BaseSpriteBatch implements IPaletteSprit
type: number, color: number, palette: Palette | undefined,
sx: number, sy: number, sw: number, sh: number, dx: number, dy: number, dw: number, dh: number
) {
if (this.capacity <= this.spritesCount) {
if (this.spritesCapacity <= this.spritesCount) {
this.flush();
}
this.index = pushQuad(
this.vertices, //this.verticesUint32,
this.vertices,
this.transform, this.index, type, getColorFloat(color, this.globalAlpha),
palette || this.defaultPalette, sx, sy, sw, sh, dx, dy, dw, dh,
palette || this.defaultPalette, sx, sy, sw, sh, dx, dy, this.depth, dw, dh,
);
this.spritesCount++;
this.tris += 2;
}
drawRect(color: number, x: number, y: number, w: number, h: number) {
if (w !== 0 && h !== 0) {
if (this.capacity <= this.spritesCount) {
if (this.spritesCapacity <= this.spritesCount) {
this.flush();
}
const s = this.rectSprite || defaultRectSprite;
this.index = pushQuad(
this.vertices, //this.verticesUint32,
this.vertices,
this.transform, this.index, s.type, getColorFloat(color, this.globalAlpha),
this.defaultPalette, s.x, s.y, s.w, s.h, x, y, w, h,
this.defaultPalette, s.x, s.y, s.w, s.h, x, y, this.depth, w, h,
);
this.spritesCount++;
this.tris += 2;
}
}
drawSprite(s: Sprite, color: number, palette: Palette | undefined, x: number, y: number) {
if (s.w !== 0 && s.h !== 0) {
if (this.capacity <= this.spritesCount) {
if (this.spritesCapacity <= this.spritesCount) {
this.flush();
}
@@ -222,21 +221,19 @@ export class PaletteSpriteBatch extends BaseSpriteBatch implements IPaletteSprit
if (w > 0 && h > 0) {
this.index = pushQuad(
this.vertices, //this.verticesUint32,
this.vertices,
this.transform, this.index, s.type, getColorFloat(color, this.globalAlpha),
palette || this.defaultPalette, sx, sy, w, h, dx, dy, w, h,
palette || this.defaultPalette, sx, sy, w, h, dx, dy, this.depth, w, h,
);
this.spritesCount++;
this.tris += 2;
}
} else {
this.index = pushQuad(
this.vertices, //this.verticesUint32,
this.vertices,
this.transform, this.index, s.type, getColorFloat(color, this.globalAlpha),
palette || this.defaultPalette, s.x, s.y, s.w, s.h, x + s.ox, y + s.oy, s.w, s.h,
palette || this.defaultPalette, s.x, s.y, s.w, s.h, x + s.ox, y + s.oy, this.depth, s.w, s.h,
);
this.spritesCount++;
this.tris += 2;
}
}
}
+3 -2
View File
@@ -24,7 +24,8 @@ varying vec2 textureCoord;
varying vec4 vColor;
void main() {
float d = clamp((1.0 - length(textureCoord)) + 0.1, 0.0, 1.0);
float m = d * d * d;
float d = clamp(1.1 - length(textureCoord), 0.0, 1.0);
float m = d * d; // * d;
gl_FragColor = vec4(m, m, m, 1) * vColor;
}
+29
View File
@@ -0,0 +1,29 @@
// VERTEX
attribute vec2 position;
attribute vec2 texcoords;
uniform mat4 transform;
uniform vec2 textureSize;
varying vec2 textureCoord;
void main() {
textureCoord = texcoords / textureSize;
gl_Position = transform * vec4(position, 0, 1);
}
// FRAGMENT
precision mediump float;
uniform sampler2D sampler1;
uniform sampler2D sampler2;
varying vec2 textureCoord;
void main() {
vec3 color1 = texture2D(sampler1, textureCoord).rgb;
vec3 color2 = texture2D(sampler2, textureCoord).rgb;
gl_FragColor = vec4(color1 * color2, 1.0);
}
+17 -5
View File
@@ -1,26 +1,33 @@
// VERTEX
attribute vec2 position;
attribute vec3 position;
attribute vec4 texcoords;
attribute vec4 vertexColor;
attribute vec4 vertexColor1;
uniform mat4 transform;
uniform vec4 lighting;
uniform float textureSize;
varying vec4 textureCoord;
varying vec4 vColor;
varying vec4 vColor1;
void main() {
textureCoord = texcoords;
textureCoord = vec4(texcoords.xy * textureSize, texcoords.zw);
// float f = texcoords.z;
// float fr = fract(texcoords.z);
// textureCoord.z = fr;
// textureCoord.w = (f - fr) / 1024.0;
vColor = vertexColor * lighting;
vColor1 = vertexColor1;
gl_Position = transform * vec4(position, 0, 1);
float depth = position.z;
#ifdef DEPTH_BUFFERED
if (vColor.a < 0.975) {
depth = 0.0;
}
#endif
gl_Position = transform * vec4(position.xy, depth, 1);
}
// FRAGMENT
@@ -30,14 +37,13 @@ precision mediump float;
uniform sampler2D sampler1; // sprite
uniform sampler2D sampler2; // palette
uniform float pixelSize;
uniform float textureSize;
varying vec4 textureCoord;
varying vec4 vColor;
varying vec4 vColor1;
void main() {
vec4 sprite = texture2D(sampler1, textureCoord.xy / textureSize);
vec4 sprite = texture2D(sampler1, textureCoord.xy);
float shade = clamp(sprite.g + vColor1.a, 0.0, 1.0);
@@ -48,4 +54,10 @@ void main() {
vec4 palette = texture2D(sampler2, vec2(paletteCoord.x + paletteIndex * pixelSize, paletteCoord.y));
gl_FragColor = vec4(palette.xyz * shade, palette.w) * vColor;
#ifdef DEPTH_BUFFERED
if (gl_FragColor.a < 0.01) {
discard;
}
#endif
}
+9 -5
View File
@@ -1,19 +1,20 @@
// VERTEX
attribute vec2 position;
attribute vec3 position;
attribute vec2 texcoords;
attribute vec4 vertexColor;
uniform mat4 transform;
uniform vec4 lighting;
uniform vec2 textureSize;
varying vec2 textureCoord;
varying vec4 vColor;
void main() {
textureCoord = texcoords;
textureCoord = texcoords / textureSize;
vColor = vertexColor * lighting;
gl_Position = transform * vec4(position, 0, 1);
gl_Position = transform * vec4(position, 1);
}
// FRAGMENT
@@ -21,11 +22,14 @@ void main() {
precision mediump float;
uniform sampler2D sampler1;
uniform float textureSize;
varying vec2 textureCoord;
varying vec4 vColor;
void main() {
gl_FragColor = texture2D(sampler1, textureCoord / textureSize) * vColor;
gl_FragColor = texture2D(sampler1, textureCoord);
#ifdef USE_COLOR
gl_FragColor *= vColor;
#endif
}
+12 -20
View File
@@ -2,11 +2,12 @@ import { Sprite, SpriteBatch as ISpriteBatch, Matrix2D } from '../common/interfa
import { BaseSpriteBatch, getColorFloat } from './baseSpriteBatch';
function vertex(
vertices: Float32Array, _verticesUint32: Uint32Array, index: number,
x: number, y: number, u: number, v: number, c: number, transform: Matrix2D
vertices: Float32Array, index: number,
x: number, y: number, depth: number, u: number, v: number, c: number, transform: Matrix2D
) {
vertices[index++] = transform[0] * x + transform[2] * y + transform[4];
vertices[index++] = transform[1] * x + transform[3] * y + transform[5];
vertices[index++] = depth;
vertices[index++] = u;
vertices[index++] = v;
vertices[index++] = c;
@@ -18,16 +19,9 @@ function vertex(
export class SpriteBatch extends BaseSpriteBatch implements ISpriteBatch {
palette = false;
depth = 0;
constructor(
gl: WebGLRenderingContext,
capacity: number,
buffer: ArrayBuffer,
vertexBuffer: WebGLBuffer,
indexBuffer: WebGLBuffer
) {
super(gl, capacity, buffer, vertexBuffer, indexBuffer, [
{ name: 'position', size: 2 },
constructor(gl: WebGLRenderingContext, vertexCapacityMax: number, indexBuffer: WebGLBuffer) {
super(gl, vertexCapacityMax, indexBuffer, [
{ name: 'position', size: 3 },
{ name: 'texcoord0', size: 2 }, // , type: gl.UNSIGNED_SHORT },
{ name: 'color', size: 4, type: gl.UNSIGNED_BYTE, normalized: true },
]);
@@ -36,7 +30,7 @@ export class SpriteBatch extends BaseSpriteBatch implements ISpriteBatch {
color: number, sx: number, sy: number, sw: number, sh: number,
dx: number, dy: number, dw: number, dh: number
) {
if (this.capacity <= this.spritesCount) {
if (this.spritesCapacity <= this.spritesCount) {
this.flush();
}
@@ -51,19 +45,17 @@ export class SpriteBatch extends BaseSpriteBatch implements ISpriteBatch {
const v2 = sy + sh;
const vertices = this.vertices;
const verticesUint32 = this.verticesUint32;
const transform = this.transform;
const index = this.index;
vertex(vertices, verticesUint32, index, dx, dy, u1, v1, c, transform);
vertex(vertices, verticesUint32, index + 5, x2, dy, u2, v1, c, transform);
vertex(vertices, verticesUint32, index + 10, x2, y2, u2, v2, c, transform);
vertex(vertices, verticesUint32, index + 15, dx, y2, u1, v2, c, transform);
vertex(vertices, index, dx, dy, this.depth, u1, v1, c, transform);
vertex(vertices, index + 6, x2, dy, this.depth, u2, v1, c, transform);
vertex(vertices, index + 12, x2, y2, this.depth, u2, v2, c, transform);
vertex(vertices, index + 18, dx, y2, this.depth, u1, v2, c, transform);
this.index += 20;
this.index += 24;
this.spritesCount++;
this.tris += 2;
}
drawRect(color: number, x: number, y: number, w: number, h: number) {
if (w && h) {
+2 -1
View File
@@ -4,7 +4,8 @@ import { createTexture, disposeTexture } from './webgl/texture2d';
export function createTexturesForSpriteSheets(gl: WebGLRenderingContext, sheets: SpriteSheet[], texture = createTexture) {
sheets.forEach(sheet => {
if (sheet.data) {
sheet.texture = texture(gl, sheet.data);
let format = sheet.isSingleChannel ? gl.LUMINANCE : gl.RGBA;
sheet.texture = texture(gl, sheet.data, format);
}
});
}

Some files were not shown because too many files have changed in this diff Show More