Skip to content

Commit

Permalink
Permissions (#196)
Browse files Browse the repository at this point in the history
* Ventilated boxcar none freight

* Permissions editor

* Bump webpack from 5.93.0 to 5.94.0

---------

Co-authored-by: Scott Anderson <[email protected]>
  • Loading branch information
scottanderson and scottanderson authored Aug 31, 2024
1 parent 2a5d0a0 commit d351ce5
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 20 deletions.
26 changes: 7 additions & 19 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 33 additions & 0 deletions ts/Permission.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,36 @@
export interface Permission {
values: boolean[];
}

export const permissionLabels = [
'Build',
'Demolish',
'Industries and Facilities',
'Purchase',
'Switches',
'Vehicles',
'Remove Vegetation',
'Rerail',
] as const satisfies ReadonlyArray<string>;

export function permissionToString(value?: Permission) {
if (typeof value === 'undefined') return 'undefined';
const {values} = value;
if (values.every(Boolean)) return 'all';
if (!values.some(Boolean)) return 'none';
return values
.map((v, i) => [v, i < permissionLabels.length ? permissionLabels[i] : `Unknown permission ${i}`])
.filter(([v]) => v)
.map(([, l]) => l)
.join(', ');
}

export function permissionEqual(
a: Permission | undefined,
b: Permission | undefined,
): boolean {
if (typeof a === 'undefined') return typeof b === 'undefined';
if (typeof b === 'undefined') return false;
if (a.values.length !== b.values.length) return false;
return a.values.every((v, i) => v === b.values[i]);
}
14 changes: 13 additions & 1 deletion ts/Studio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {createFilter} from './Filter';
import {calculateSteepestGrade} from './Grade';
import {GvasString, GvasText, gvasToString} from './Gvas';
import {createPager} from './Pager';
import {Permission} from './Permission';
import {Frame, NumericFrameState, Railroad, SplineType, Quadruplet} from './Railroad';
import {MapLayers, RailroadMap} from './RailroadMap';
import {Rotator} from './Rotator';
Expand All @@ -13,6 +14,7 @@ import {
editIndustryProducts,
editIndustryType,
editNumber,
editPermissions,
editQuaternion,
editRotator,
editSlider,
Expand Down Expand Up @@ -1533,7 +1535,10 @@ export class Studio {
table.appendChild(thead);
let tr = document.createElement('tr');
thead.appendChild(tr);
for (const columnHeader of ['Steam ID', 'Name', 'Money', 'XP', 'Location']) {
const hasPermissions = this.railroad.players.some((player) => typeof player.permissions !== 'undefined');
const columnHeaders = ['Steam ID', 'Name', 'Money', 'XP', 'Location'];
if (hasPermissions) columnHeaders.push('Permissions');
for (const columnHeader of columnHeaders) {
const th = document.createElement('th');
th.textContent = columnHeader;
tr.appendChild(th);
Expand Down Expand Up @@ -1573,6 +1578,13 @@ export class Studio {
td.replaceChildren(editNumber(this, player.rotation, {min: '-180', max: '180'}, setPlayerRotation));
tr.appendChild(td);
}
// Permissions
if (hasPermissions) {
td = document.createElement('td');
const setPlayerPermissions = (p: Permission | undefined) => player.permissions = p;
td.appendChild(editPermissions(this, player.permissions, setPlayerPermissions));
tr.appendChild(td);
}
}
}

Expand Down
71 changes: 71 additions & 0 deletions ts/StudioEditor.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {GvasString, GvasText, gvasToString} from './Gvas';
import {IndustryName, IndustryNames, IndustryType, industryNames, isIndustryName} from './industries';
import {Permission, permissionEqual, permissionLabels, permissionToString} from './Permission';
import {Quaternion} from './Quaternion';
import {Quadruplet} from './Railroad';
import {Rotator} from './Rotator';
Expand Down Expand Up @@ -111,6 +112,76 @@ export function editNumber(
return pre;
}

export function editPermissions(
studio: Studio,
value: Permission | undefined,
saveValue: (value: Permission | undefined) => Permission | undefined,
) {
const formatValue = () => permissionToString(value);
const vstack = document.createElement('div');
vstack.classList.add('vstack', 'gap-2');
const inputs: HTMLInputElement[] = [];
const fromValue: number = value?.values.length ?? 0;
const fromKnown = permissionLabels.length;
for (let i = 0; i < Math.max(fromValue, fromKnown); i++) {
const hstack = document.createElement('div');
hstack.classList.add('hstack', 'gap-2');
const input = document.createElement('input');
input.id = `permission${i}-${Math.random().toString(36).substr(2, 16)}`;
input.type = 'checkbox';
input.title = i < permissionLabels.length ? permissionLabels[i] : `Unknown permission ${i}`;
input.checked = typeof value !== 'undefined' && value.values[i];
input.addEventListener('keydown', (event: KeyboardEvent) => {
if (event.key === 'Enter') {
event.preventDefault();
save();
} else if (event.key === 'Escape') {
event.preventDefault();
cancel();
}
});
inputs.push(input);
const label = document.createElement('label');
label.setAttribute('for', input.id);
label.innerText = input.title;
hstack.replaceChildren(input, label);
vstack.appendChild(hstack);
}
const getValue = (): Permission | undefined => {
const values = inputs.map((input) => input.checked);
return {values};
};
const onSave = () => {
value = getValue();
value = saveValue(value);
return onCancel();
};
const onCancel = () => {
const inputValue = getValue();
if (inputValue !== value) {
// Restore the original value
inputs.forEach((input, i) => {
input.checked = typeof value !== 'undefined' && value.values[i];
});
if (permissionEqual(inputValue, getValue())) {
// No effect. Close the edit control
return false;
}
return true;
}
// Close the edit control
return false;
};
const preview = document.createElement('pre');
preview.classList.add('mb-0');
preview.textContent = formatValue();
const form = document.createElement('form');
form.classList.add('form-group', 'w-100');
form.replaceChildren(vstack);
const [pre, save, cancel] = saveContext(studio, form, onSave, onCancel, formatValue);
return pre;
}

export function editSlider(
studio: Studio,
value: number,
Expand Down
1 change: 1 addition & 0 deletions ts/frames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1699,6 +1699,7 @@ export const cargoLimits = {
},
VentilatedBoxcarCC: {
['EFreightType::Meat']: 36,
['EFreightType::None']: 0,
},
} as const satisfies PRO<FrameType, PRO<CargoType, number>>;

Expand Down

0 comments on commit d351ce5

Please sign in to comment.