Merge remote-tracking branch 'misskey-dev/develop' into io

This commit is contained in:
まっちゃとーにゅ 2024-01-17 21:42:04 +09:00
commit 50f4ae1673
No known key found for this signature in database
GPG key ID: 143DE582A97FE052
13 changed files with 97 additions and 42 deletions

View file

@ -23,6 +23,7 @@
- Feat: 音声・映像プレイヤーを追加 - Feat: 音声・映像プレイヤーを追加
- Feat: 絵文字の詳細ダイアログを追加 - Feat: 絵文字の詳細ダイアログを追加
- Feat: 枠線をつけるMFM`$[border.width=1,style=solid,color=fff,radius=0 ...]`を追加 - Feat: 枠線をつけるMFM`$[border.width=1,style=solid,color=fff,radius=0 ...]`を追加
- デフォルトで枠線からはみ出る部分が隠されるようにしました。初期と同じ挙動にするには`$[border.noclip`が必要です
- Enhance: ハッシュタグ入力時に、本文の末尾の行に何も書かれていない場合は新たにスペースを追加しないように - Enhance: ハッシュタグ入力時に、本文の末尾の行に何も書かれていない場合は新たにスペースを追加しないように
- Enhance: チャンネルノートのピン留めをノートのメニューからできるように - Enhance: チャンネルノートのピン留めをノートのメニューからできるように
- Enhance: 管理者の場合はAPI tokenの発行画面で管理機能に関する権限を付与できるように - Enhance: 管理者の場合はAPI tokenの発行画面で管理機能に関する権限を付与できるように
@ -30,10 +31,12 @@
- 配列の範囲外・非整数のインデックスへの代入が完全禁止になるので注意 - 配列の範囲外・非整数のインデックスへの代入が完全禁止になるので注意
- Enhance: 絵文字ピッカー・オートコンプリートで、完全一致した絵文字を優先的に表示するように - Enhance: 絵文字ピッカー・オートコンプリートで、完全一致した絵文字を優先的に表示するように
- Enhance: Playの説明欄にMFMを使えるように - Enhance: Playの説明欄にMFMを使えるように
- Enhance: チャンネルノートの場合は詳細ページからその前後のノートを見れるように
- Fix: ネイティブモードの絵文字がモノクロにならないように - Fix: ネイティブモードの絵文字がモノクロにならないように
- Fix: v2023.12.0で追加された「モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能」が管理画面上で正しく表示されていない問題を修正 - Fix: v2023.12.0で追加された「モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能」が管理画面上で正しく表示されていない問題を修正
- Fix: AiScriptの`readline`関数が不正な値を返すことがある問題のv2023.12.0時点での修正がPlay以外に適用されていないのを修正 - Fix: AiScriptの`readline`関数が不正な値を返すことがある問題のv2023.12.0時点での修正がPlay以外に適用されていないのを修正
- Fix: v2023.12.1で追加された`$[clickable ...]`および`onClickEv`が正しく機能していないのを修正 - Fix: v2023.12.1で追加された`$[clickable ...]`および`onClickEv`が正しく機能していないのを修正
- Enhance: ページ遷移時にPlayerを閉じるように
### Server ### Server
- Enhance: 連合先のレートリミットに引っかかった際にリトライするようになりました - Enhance: 連合先のレートリミットに引っかかった際にリトライするようになりました
@ -45,6 +48,7 @@
- Fix: `notes/create`で、`text`が空白文字のみで構成されているか`null`であって、かつ`text`だけであるリクエストに対するレスポンスが400になるように変更 - Fix: `notes/create`で、`text`が空白文字のみで構成されているか`null`であって、かつ`text`だけであるリクエストに対するレスポンスが400になるように変更
- Fix: `notes/create`で、`text`が空白文字のみで構成されていてかつリノート、ファイルまたは投票を含んでいるリクエストに対するレスポンスの`text`が`""`から`null`になるように変更 - Fix: `notes/create`で、`text`が空白文字のみで構成されていてかつリノート、ファイルまたは投票を含んでいるリクエストに対するレスポンスの`text`が`""`から`null`になるように変更
- Fix: ipv4とipv6の両方が利用可能な環境でallowedPrivateNetworksが設定されていた場合プライベートipの検証ができていなかった問題を修正 - Fix: ipv4とipv6の両方が利用可能な環境でallowedPrivateNetworksが設定されていた場合プライベートipの検証ができていなかった問題を修正
- Fix: properly handle cc followers
## 2023.12.2 ## 2023.12.2

View file

@ -58,7 +58,7 @@ export class ApAudienceService {
}; };
} }
if (toGroups.followers.length > 0) { if (toGroups.followers.length > 0 || ccGroups.followers.length > 0) {
return { return {
visibility: 'followers', visibility: 'followers',
mentionedUsers, mentionedUsers,

View file

@ -10,9 +10,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSpacer> <MkSpacer>
<div style="display: flex; flex-direction: column; gap: 1em;"> <div style="display: flex; flex-direction: column; gap: 1em;">
<div :class="$style.emojiImgWrapper"> <div :class="$style.emojiImgWrapper">
<MkCustomEmoji :name="emoji.name" :normal="true" style="height: 100%;"></MkCustomEmoji> <MkCustomEmoji :name="emoji.name" :normal="true" :useOriginalSize="true" style="height: 100%;"></MkCustomEmoji>
</div> </div>
<MkKeyValue> <MkKeyValue :copy="`:${emoji.name}:`">
<template #key>{{ i18n.ts.name }}</template> <template #key>{{ i18n.ts.name }}</template>
<template #value>{{ emoji.name }}</template> <template #value>{{ emoji.name }}</template>
</MkKeyValue> </MkKeyValue>
@ -41,12 +41,12 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkKeyValue> </MkKeyValue>
<MkKeyValue> <MkKeyValue>
<template #key>{{ i18n.ts.license }}</template> <template #key>{{ i18n.ts.license }}</template>
<template #value>{{ emoji.license ?? i18n.ts.none }}</template> <template #value><Mfm :text="emoji.license ?? i18n.ts.none" /></template>
</MkKeyValue> </MkKeyValue>
<MkKeyValue :copy="emoji.url"> <MkKeyValue :copy="emoji.url">
<template #key>{{ i18n.ts.emojiUrl }}</template> <template #key>{{ i18n.ts.emojiUrl }}</template>
<template #value> <template #value>
<a :href="emoji.url" rel="nofollow noopener" target="_blank">{{ emoji.url }}</a> <MkLink :url="emoji.url" target="_blank">{{ emoji.url }}</MkLink>
</template> </template>
</MkKeyValue> </MkKeyValue>
</div> </div>
@ -61,6 +61,7 @@ import { defineProps, shallowRef } from 'vue';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import MkModalWindow from '@/components/MkModalWindow.vue'; import MkModalWindow from '@/components/MkModalWindow.vue';
import MkKeyValue from '@/components/MkKeyValue.vue'; import MkKeyValue from '@/components/MkKeyValue.vue';
import MkLink from './MkLink.vue';
const props = defineProps<{ const props = defineProps<{
emoji: Misskey.entities.EmojiDetailed, emoji: Misskey.entities.EmojiDetailed,
}>(); }>();
@ -94,6 +95,7 @@ const cancel = () => {
.alias { .alias {
display: inline-block; display: inline-block;
word-break: break-all;
padding: 3px 10px; padding: 3px 10px;
background-color: var(--X5); background-color: var(--X5);
border: solid 1px var(--divider); border: solid 1px var(--divider);

View file

@ -83,7 +83,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { defineAsyncComponent, onUnmounted, ref } from 'vue'; import { defineAsyncComponent, onDeactivated, onUnmounted, ref } from 'vue';
import type { summaly } from '@misskey-dev/summaly'; import type { summaly } from '@misskey-dev/summaly';
import { url as local } from '@/config.js'; import { url as local } from '@/config.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
@ -131,6 +131,10 @@ const embedId = `embed${Math.random().toString().replace(/\D/, '')}`;
const tweetHeight = ref(150); const tweetHeight = ref(150);
const unknownUrl = ref(false); const unknownUrl = ref(false);
onDeactivated(() => {
playerEnabled.value = false;
});
const requestUrl = new URL(props.url); const requestUrl = new URL(props.url);
if (!['http:', 'https:'].includes(requestUrl.protocol)) throw new Error('invalid url'); if (!['http:', 'https:'].includes(requestUrl.protocol)) throw new Error('invalid url');

View file

@ -266,7 +266,7 @@ export default function(props: MfmProps, context: SetupContext<MfmEvents>) {
) b_style = 'solid'; ) b_style = 'solid';
const width = parseFloat(token.props.args.width ?? '1'); const width = parseFloat(token.props.args.width ?? '1');
const radius = parseFloat(token.props.args.radius ?? '0'); const radius = parseFloat(token.props.args.radius ?? '0');
style = `border: ${width}px ${b_style} ${color}; border-radius: ${radius}px`; style = `border: ${width}px ${b_style} ${color}; border-radius: ${radius}px;${token.props.args.noclip ? '' : ' overflow: clip;'}`;
break; break;
} }
case 'ruby': { case 'ruby': {

View file

@ -22,7 +22,8 @@
style-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';
img-src 'self' data: blob: www.google.com xn--931a.moe localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000; img-src 'self' data: blob: www.google.com xn--931a.moe localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000;
media-src 'self' localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000; media-src 'self' localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000;
connect-src 'self' localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000 https://newassets.hcaptcha.com;" connect-src 'self' localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000 https://newassets.hcaptcha.com;
frame-src *;"
/> />
<meta property="og:site_name" content="[DEV BUILD] Misskey" /> <meta property="og:site_name" content="[DEV BUILD] Misskey" />
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">

View file

@ -268,6 +268,7 @@ definePageMetadata(computed(() => channel.value ? {
.footer { .footer {
-webkit-backdrop-filter: var(--blur, blur(15px)); -webkit-backdrop-filter: var(--blur, blur(15px));
backdrop-filter: var(--blur, blur(15px)); backdrop-filter: var(--blur, blur(15px));
background: var(--acrylicBg);
border-top: solid 0.5px var(--divider); border-top: solid 0.5px var(--divider);
} }

View file

@ -496,7 +496,7 @@ const SWEETS_MONOS: FrontendMonoDefinition[] = [{
}]; }];
const props = defineProps<{ const props = defineProps<{
gameMode: 'normal' | 'square' | 'yen' | 'sweets'; gameMode: 'normal' | 'square' | 'yen' | 'sweets' | 'space';
mute: boolean; mute: boolean;
}>(); }>();
@ -509,6 +509,7 @@ const monoDefinitions = computed(() => {
props.gameMode === 'square' ? SQUARE_MONOS : props.gameMode === 'square' ? SQUARE_MONOS :
props.gameMode === 'yen' ? YEN_MONOS : props.gameMode === 'yen' ? YEN_MONOS :
props.gameMode === 'sweets' ? SWEETS_MONOS : props.gameMode === 'sweets' ? SWEETS_MONOS :
props.gameMode === 'space' ? NORAML_MONOS :
[] as never; [] as never;
}); });

View file

@ -28,6 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<option value="square">SQUARE</option> <option value="square">SQUARE</option>
<option value="yen">YEN</option> <option value="yen">YEN</option>
<option value="sweets">SWEETS</option> <option value="sweets">SWEETS</option>
<!--<option value="space">SPACE</option>-->
</MkSelect> </MkSelect>
<MkButton primary gradate large rounded inline @click="start">{{ i18n.ts.start }}</MkButton> <MkButton primary gradate large rounded inline @click="start">{{ i18n.ts.start }}</MkButton>
</div> </div>
@ -94,7 +95,7 @@ import MkSelect from '@/components/MkSelect.vue';
import MkSwitch from '@/components/MkSwitch.vue'; import MkSwitch from '@/components/MkSwitch.vue';
import { misskeyApiGet } from '@/scripts/misskey-api.js'; import { misskeyApiGet } from '@/scripts/misskey-api.js';
const gameMode = ref<'normal' | 'square' | 'yen' | 'sweets'>('normal'); const gameMode = ref<'normal' | 'square' | 'yen' | 'sweets' | 'space'>('normal');
const gameStarted = ref(false); const gameStarted = ref(false);
const mute = ref(false); const mute = ref(false);
const ranking = ref(null); const ranking = ref(null);
@ -108,6 +109,7 @@ function getScoreUnit(gameMode: string) {
gameMode === 'square' ? 'pt' : gameMode === 'square' ? 'pt' :
gameMode === 'yen' ? '円' : gameMode === 'yen' ? '円' :
gameMode === 'sweets' ? 'kcal' : gameMode === 'sweets' ? 'kcal' :
gameMode === 'space' ? 'pt' :
'' as never; '' as never;
} }

View file

@ -44,7 +44,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.setMultipleBySeparatingWithSpace }} {{ i18n.ts.setMultipleBySeparatingWithSpace }}
</template> </template>
</MkInput> </MkInput>
<MkTextarea v-model="license"> <MkTextarea v-model="license" :mfmAutocomplete="true">
<template #label>{{ i18n.ts.license }}</template> <template #label>{{ i18n.ts.license }}</template>
</MkTextarea> </MkTextarea>
<MkFolder> <MkFolder>

View file

@ -11,11 +11,14 @@ SPDX-License-Identifier: AGPL-3.0-only
<Transition :name="defaultStore.state.animation ? 'fade' : ''" mode="out-in"> <Transition :name="defaultStore.state.animation ? 'fade' : ''" mode="out-in">
<div v-if="note"> <div v-if="note">
<div v-if="showNext" class="_margin"> <div v-if="showNext" class="_margin">
<MkNotes class="" :pagination="nextPagination" :noGap="true" :disableAutoLoad="true"/> <MkNotes class="" :pagination="showNext === 'channel' ? nextChannelPagination : nextUserPagination" :noGap="true" :disableAutoLoad="true"/>
</div> </div>
<div class="_margin"> <div class="_margin">
<MkButton v-if="!showNext" :class="$style.loadNext" @click="showNext = true"><i class="ti ti-chevron-up"></i></MkButton> <div v-if="!showNext" class="_buttons" :class="$style.loadNext">
<MkButton v-if="note.channelId" rounded :class="$style.loadButton" @click="showNext = 'channel'"><i class="ti ti-chevron-up"></i> <i class="ti ti-device-tv"></i></MkButton>
<MkButton rounded :class="$style.loadButton" @click="showNext = 'user'"><i class="ti ti-chevron-up"></i> <i class="ti ti-user"></i></MkButton>
</div>
<div class="_margin _gaps_s"> <div class="_margin _gaps_s">
<MkRemoteCaution v-if="note.user.host != null" :href="note.url ?? note.uri"/> <MkRemoteCaution v-if="note.user.host != null" :href="note.url ?? note.uri"/>
<MkNoteDetailed :key="note.id" v-model:note="note" :class="$style.note"/> <MkNoteDetailed :key="note.id" v-model:note="note" :class="$style.note"/>
@ -28,11 +31,14 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkA> </MkA>
</div> </div>
</div> </div>
<MkButton v-if="!showPrev" :class="$style.loadPrev" @click="showPrev = true"><i class="ti ti-chevron-down"></i></MkButton> <div v-if="!showPrev" class="_buttons" :class="$style.loadPrev">
<MkButton v-if="note.channelId" rounded :class="$style.loadButton" @click="showPrev = 'channel'"><i class="ti ti-chevron-down"></i> <i class="ti ti-device-tv"></i></MkButton>
<MkButton rounded :class="$style.loadButton" @click="showPrev = 'user'"><i class="ti ti-chevron-down"></i> <i class="ti ti-user"></i></MkButton>
</div>
</div> </div>
<div v-if="showPrev" class="_margin"> <div v-if="showPrev" class="_margin">
<MkNotes class="" :pagination="prevPagination" :noGap="true"/> <MkNotes class="" :pagination="showPrev === 'channel' ? prevChannelPagination : prevUserPagination" :noGap="true"/>
</div> </div>
</div> </div>
<MkError v-else-if="error" @retry="fetchNote()"/> <MkError v-else-if="error" @retry="fetchNote()"/>
@ -46,6 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup> <script lang="ts" setup>
import { computed, watch, ref } from 'vue'; import { computed, watch, ref } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import type { Paging } from '@/components/MkPagination.vue';
import MkNoteDetailed from '@/components/MkNoteDetailed.vue'; import MkNoteDetailed from '@/components/MkNoteDetailed.vue';
import MkNotes from '@/components/MkNotes.vue'; import MkNotes from '@/components/MkNotes.vue';
import MkRemoteCaution from '@/components/MkRemoteCaution.vue'; import MkRemoteCaution from '@/components/MkRemoteCaution.vue';
@ -63,27 +70,46 @@ const props = defineProps<{
const note = ref<null | Misskey.entities.Note>(); const note = ref<null | Misskey.entities.Note>();
const clips = ref<Misskey.entities.Clip[]>(); const clips = ref<Misskey.entities.Clip[]>();
const showPrev = ref(false); const showPrev = ref<'user' | 'channel' | false>(false);
const showNext = ref(false); const showNext = ref<'user' | 'channel' | false>(false);
const error = ref(); const error = ref();
const prevPagination = { const prevUserPagination: Paging = {
endpoint: 'users/notes' as const, endpoint: 'users/notes',
limit: 10, limit: 10,
params: computed(() => note.value ? ({ params: computed(() => note.value ? ({
userId: note.value.userId, userId: note.value.userId,
untilId: note.value.id, untilId: note.value.id,
}) : null), }) : undefined),
}; };
const nextPagination = { const nextUserPagination: Paging = {
reversed: true, reversed: true,
endpoint: 'users/notes' as const, endpoint: 'users/notes',
limit: 10, limit: 10,
params: computed(() => note.value ? ({ params: computed(() => note.value ? ({
userId: note.value.userId, userId: note.value.userId,
sinceId: note.value.id, sinceId: note.value.id,
}) : null), }) : undefined),
};
const prevChannelPagination: Paging = {
endpoint: 'channels/timeline',
limit: 10,
params: computed(() => note.value ? ({
channelId: note.value.channelId,
untilId: note.value.id,
}) : undefined),
};
const nextChannelPagination: Paging = {
reversed: true,
endpoint: 'channels/timeline',
limit: 10,
params: computed(() => note.value ? ({
channelId: note.value.channelId,
sinceId: note.value.id,
}) : undefined),
}; };
function fetchNote() { function fetchNote() {
@ -139,9 +165,7 @@ definePageMetadata(computed(() => note.value ? {
.loadNext, .loadNext,
.loadPrev { .loadPrev {
min-width: 0; justify-content: center;
margin: 0 auto;
border-radius: 999px;
} }
.loadNext { .loadNext {
@ -152,6 +176,10 @@ definePageMetadata(computed(() => note.value ? {
margin-top: var(--margin); margin-top: var(--margin);
} }
.loadButton {
min-width: 0;
}
.note { .note {
border-radius: var(--radius); border-radius: var(--radius);
background: var(--panel); background: var(--panel);

View file

@ -65,11 +65,12 @@ definePageMetadata(computed(() => ({
.footer { .footer {
-webkit-backdrop-filter: var(--blur, blur(15px)); -webkit-backdrop-filter: var(--blur, blur(15px));
backdrop-filter: var(--blur, blur(15px)); backdrop-filter: var(--blur, blur(15px));
background: var(--acrylicBg);
border-top: solid 0.5px var(--divider); border-top: solid 0.5px var(--divider);
display: flex; display: flex;
} }
.button { .button {
margin: 0 auto var(--margin) auto; margin: 0 auto;
} }
</style> </style>

View file

@ -31,7 +31,7 @@ type Log = {
operation: 'surrender'; operation: 'surrender';
}; };
const NORMAL_BASE_SIZE = 30; const NORMAL_BASE_SIZE = 32;
const NORAML_MONOS: Mono[] = [{ const NORAML_MONOS: Mono[] = [{
id: '9377076d-c980-4d83-bdaf-175bc58275b7', id: '9377076d-c980-4d83-bdaf-175bc58275b7',
level: 10, level: 10,
@ -114,7 +114,7 @@ const NORAML_MONOS: Mono[] = [{
dropCandidate: true, dropCandidate: true,
}]; }];
const YEN_BASE_SIZE = 30; const YEN_BASE_SIZE = 32;
const YEN_SATSU_BASE_SIZE = 70; const YEN_SATSU_BASE_SIZE = 70;
const YEN_MONOS: Mono[] = [{ const YEN_MONOS: Mono[] = [{
id: '880f9bd9-802f-4135-a7e1-fd0e0331f726', id: '880f9bd9-802f-4135-a7e1-fd0e0331f726',
@ -1003,7 +1003,7 @@ export class DropAndFusionGame extends EventEmitter<{
private tickCallbackQueue: { frame: number; callback: () => void; }[] = []; private tickCallbackQueue: { frame: number; callback: () => void; }[] = [];
private overflowCollider: Matter.Body; private overflowCollider: Matter.Body;
private isGameOver = false; private isGameOver = false;
private gameMode: 'normal' | 'yen' | 'square' | 'sweets'; private gameMode: 'normal' | 'yen' | 'square' | 'sweets' | 'space';
private rng: () => number; private rng: () => number;
private logs: Log[] = []; private logs: Log[] = [];
@ -1031,6 +1031,7 @@ export class DropAndFusionGame extends EventEmitter<{
case 'yen': return YEN_MONOS; case 'yen': return YEN_MONOS;
case 'square': return SQUARE_MONOS; case 'square': return SQUARE_MONOS;
case 'sweets': return SWEETS_MONOS; case 'sweets': return SWEETS_MONOS;
case 'space': return NORAML_MONOS;
default: throw new Error('unknown game mode'); default: throw new Error('unknown game mode');
} }
} }
@ -1072,13 +1073,15 @@ export class DropAndFusionGame extends EventEmitter<{
this.getMonoRenderOptions = env.getMonoRenderOptions ?? null; this.getMonoRenderOptions = env.getMonoRenderOptions ?? null;
this.rng = seedrandom(env.seed); this.rng = seedrandom(env.seed);
// sweetsモードは重いため
const physicsQualityFactor = this.gameMode === 'sweets' ? 4 : this.PHYSICS_QUALITY_FACTOR;
this.engine = Matter.Engine.create({ this.engine = Matter.Engine.create({
constraintIterations: 2 * this.PHYSICS_QUALITY_FACTOR, constraintIterations: 2 * physicsQualityFactor,
positionIterations: 6 * this.PHYSICS_QUALITY_FACTOR, positionIterations: 6 * physicsQualityFactor,
velocityIterations: 4 * this.PHYSICS_QUALITY_FACTOR, velocityIterations: 4 * physicsQualityFactor,
gravity: { gravity: {
x: 0, x: 0,
y: 1, y: this.gameMode === 'space' ? 0.0125 : 1,
}, },
timing: { timing: {
timeScale: 2, timeScale: 2,
@ -1093,7 +1096,7 @@ export class DropAndFusionGame extends EventEmitter<{
label: '_wall_', label: '_wall_',
isStatic: true, isStatic: true,
friction: 0.7, friction: 0.7,
slop: 1.0, slop: this.gameMode === 'space' ? 0.01 : 0.7,
render: { render: {
strokeStyle: 'transparent', strokeStyle: 'transparent',
fillStyle: 'transparent', fillStyle: 'transparent',
@ -1131,13 +1134,12 @@ export class DropAndFusionGame extends EventEmitter<{
private createBody(mono: Mono, x: number, y: number) { private createBody(mono: Mono, x: number, y: number) {
const options: Matter.IBodyDefinition = { const options: Matter.IBodyDefinition = {
label: mono.id, label: mono.id,
//density: 0.0005, density: this.gameMode === 'space' ? 0.01 : ((mono.sizeX * mono.sizeY) / 10000),
density: ((mono.sizeX + mono.sizeY) / 2) / 1000, restitution: this.gameMode === 'space' ? 0.5 : 0.2,
restitution: 0.2, frictionAir: this.gameMode === 'space' ? 0 : 0.01,
frictionAir: 0.01, friction: this.gameMode === 'space' ? 0.5 : 0.7,
friction: 0.7, frictionStatic: this.gameMode === 'space' ? 0 : 5,
frictionStatic: 5, slop: this.gameMode === 'space' ? 0.01 : 0.7,
slop: 1.0,
//mass: 0, //mass: 0,
render: this.getMonoRenderOptions ? this.getMonoRenderOptions(mono) : undefined, render: this.getMonoRenderOptions ? this.getMonoRenderOptions(mono) : undefined,
}; };
@ -1327,6 +1329,15 @@ export class DropAndFusionGame extends EventEmitter<{
operation: 'drop', operation: 'drop',
x: inputX, x: inputX,
}); });
// add force
if (this.gameMode === 'space') {
Matter.Body.applyForce(body, body.position, {
x: 0,
y: (Math.PI * head.mono.sizeX * head.mono.sizeY) / 65536,
});
}
Matter.Composite.add(this.engine.world, body); Matter.Composite.add(this.engine.world, body);
this.fusionReadyBodyIds.push(body.id); this.fusionReadyBodyIds.push(body.id);