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
@@ -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() { },