Merge branch 'archive' into archive-update-v0.53.2

This commit is contained in:
Eliot Partridge
2019-09-17 22:20:48 -05:00
113 changed files with 3698 additions and 3719 deletions
@@ -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"
@@ -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 -28
View File
@@ -2,38 +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
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 pixel.horse.
If you support pixel.horse 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 -1
View File
@@ -37,7 +37,7 @@
.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")
+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, twitterLink, discordLink, copyrightName } from '../../client/data';
import { PonyTownGame } from '../../client/game';
import { faTwitter, faPatreon, faEnvelope, faCog, faHome, faGamepad, faInfoCircle, faHorseHead, faDiscord } from '../../client/icons';
import { InstallService } from '../services/installService';
import { faTwitter, faPatreon, faDiscord, 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';
@@ -50,7 +49,7 @@ export class App implements OnInit, OnDestroy {
readonly discordIcon = faDiscord;
readonly cogIcon = faCog;
readonly homeIcon = faHome;
readonly helpIcon = faGamepad;
readonly helpIcon = faQuestionCircle;
readonly aboutIcon = faInfoCircle;
readonly charactersIcon = faHorseHead;
readonly contactEmail = contactEmail;
@@ -68,12 +67,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;
@@ -14,7 +14,7 @@ page-loader(*ngIf="!playing")
h2.sr-only Character selection
.mx-auto(style="max-width: 400px;")
discord-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);
})
}
}
+2 -1
View File
@@ -20,12 +20,13 @@ div(*ngIf="!playing")
div(*ngIf="!!account")
.form-group
discord-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
@@ -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;
@@ -14,6 +14,7 @@ import { sampleMessages } from '../../../common/debugData';
import { findEntityById } from '../../../common/worldMap';
import { colorToRGBA, rgb2hsl, HSL, hsl2CSS } from '../../../common/color';
import * as moment from 'moment';
import { isMobile } from '../../../client/data';
interface IndexEntryUser {
id: number;
@@ -387,7 +388,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;
@@ -645,7 +649,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;
@@ -28,14 +28,14 @@
position: absolute;
right: 0;
bottom: 0;
height: 70px;
height: 80px;
width: 100px;
overflow: hidden;
border-radius: $border-radius-lg - 2px;
> discord-pony {
position: absolute;
bottom: -95px;
bottom: -80px;
left: -20px;
}
@@ -13,7 +13,7 @@ export class InstallButton {
constructor(private installService: InstallService) {
}
get canInstall() {
return this.installService.canInstall;
return false;
}
get isMobile() {
return isMobile;
@@ -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);
@@ -125,6 +157,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 -2
View File
@@ -23,7 +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 { SupporterPony } from './supporter-pony/supporter-pony';
import { DiscordButton } from './discord-button/discord-button';
import { DiscordPony } from './discord-pony/discord-pony';
import { SetSelection, SetOutlineHidden } from './set-selection/set-selection';
@@ -74,6 +73,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,
@@ -90,9 +91,10 @@ const declarations = [
SliderBar,
SpriteBox,
SpriteSelection,
SupporterPony,
DiscordButton,
DiscordPony,
VisitPTButton,
VisitPTPony,
SetSelection,
SetOutlineHidden,
CheckBox,
@@ -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")
@@ -0,0 +1,51 @@
@import '../../../../styles/partials/variables';
:host {
display: block;
}
.btn {
position: relative;
@media (max-width: 320px) {
white-space: normal;
}
}
.btn-pt {
padding: 1.0rem;
font-weight: 500;
//height: 60px;
}
.pt-logo {
position: absolute;
left: 8px;
top: 6px;
width: 37px;
height: 47px;
@media (max-width: 320px) {
display: none;
}
}
.visit-pt-pony {
position: absolute;
right: 0;
bottom: 0;
height: 80px;
width: 100px;
overflow: hidden;
border-radius: $border-radius-lg - 2px;
> visit-pt-pony {
position: absolute;
bottom: -80px;
left: -20px;
}
@media (max-width: 360px) {
display: none;
}
}
@@ -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() { },