Commit e831f456 authored by Chunchi Che's avatar Chunchi Che

Merge branch 'feat/counter' into 'main'

Feat/counter

See merge request !154
parents f586fbb6 ec0e0263
Pipeline #20936 passed with stages
in 19 minutes and 18 seconds
neos-protobuf @ 76135099
Subproject commit 12d819b3b0c69b334f475b01fdbf2f7fc6f5e2d9
Subproject commit 76135099fbeb0a69b7f8562bd755975593794e4b
This diff is collapsed.
......@@ -52,3 +52,5 @@ export const MSG_WAITING = 3;
export const MSG_UPDATE_DATA = 6;
export const MSG_RELOAD_FIELD = 162;
export const MSG_SELECT_SUM = 23;
export const MSG_ADD_COUNTER = 101;
export const MSG_REMOVE_COUNTER = 102;
import { ygopro } from "../../../idl/ocgcore";
import { BufferReaderExt } from "../../bufferIO";
import MsgUpdateCounter = ygopro.StocGameMessage.MsgUpdateCounter;
/*
* Msg Add Counter
* @param - TODO
*
* @usage - TODO
* */
export default (data: Uint8Array) => {
const reader = new BufferReaderExt(data);
const counterType = reader.inner.readUint16();
const location = reader.readCardShortLocation();
const count = reader.inner.readUint16();
return new MsgUpdateCounter({
counter_type: counterType,
location,
action_type: MsgUpdateCounter.ActionType.ADD,
count,
});
};
......@@ -28,6 +28,8 @@ import MsgWin from "./win";
import MsgUpdateDataAdapter from "./updateData";
import MsgReloadFieldAdapter from "./reloadField";
import MsgSelectSum from "./selectSum";
import MsgAddCounter from "./addCounter";
import MsgRemoveCounter from "./removeCounter";
import PENETRATE from "./penetrate";
/*
......@@ -166,6 +168,16 @@ export default class GameMsgAdapter implements StocAdapter {
break;
}
case GAME_MSG.MSG_ADD_COUNTER: {
gameMsg.update_counter = MsgAddCounter(gameData);
break;
}
case GAME_MSG.MSG_REMOVE_COUNTER: {
gameMsg.update_counter = MsgRemoveCounter(gameData);
break;
}
default: {
gameMsg.unimplemented = new ygopro.StocGameMessage.MsgUnimplemented({
command: func,
......
import { ygopro } from "../../../idl/ocgcore";
import { BufferReaderExt } from "../../bufferIO";
import MsgUpdateCounter = ygopro.StocGameMessage.MsgUpdateCounter;
/*
* Msg Remove Counter
* @param - TODO
*
* @usage - TODO
* */
export default (data: Uint8Array) => {
const reader = new BufferReaderExt(data);
const counterType = reader.inner.readUint16();
const location = reader.readCardShortLocation();
const count = reader.inner.readUint16();
return new MsgUpdateCounter({
counter_type: counterType,
location,
action_type: MsgUpdateCounter.ActionType.REMOVE,
count,
});
};
......@@ -16,7 +16,7 @@ export async function initStrings() {
}
}
export function fetchStrings(region: string, id: number): string {
export function fetchStrings(region: string, id: string | number): string {
return localStorage.getItem(`${region}_${id}`) || "";
}
......
......@@ -51,6 +51,7 @@ export const cemeteryCase = (builder: ActionReducerMapBuilder<DuelState>) => {
sequence,
},
idleInteractivities: [],
counters: {},
};
if (judgeSelf(controler, state)) {
extendState(state.meCemetery, newCemetery);
......
......@@ -24,6 +24,7 @@ export const initDeckImpl: CaseReducer<
location: ygopro.CardZone.DECK,
},
idleInteractivities: [],
counters: {},
});
}
......
......@@ -53,6 +53,7 @@ export const exclusionCase = (builder: ActionReducerMapBuilder<DuelState>) => {
sequence,
},
idleInteractivities: [],
counters: {},
};
if (judgeSelf(controler, state)) {
extendState(state.meExclusion, newExclusion);
......
......@@ -40,6 +40,7 @@ export const extraDeckCase = (builder: ActionReducerMapBuilder<DuelState>) => {
location: ygopro.CardZone.EXTRA,
},
idleInteractivities: [],
counters: {},
};
});
state.meExtraDeck = { inner: cards };
......@@ -64,6 +65,7 @@ export const extraDeckCase = (builder: ActionReducerMapBuilder<DuelState>) => {
sequence,
},
idleInteractivities: [],
counters: {},
};
const extraDeck = judgeSelf(controler, state)
? state.meExtraDeck
......
......@@ -34,6 +34,7 @@ export interface CardState {
sequence: number;
}>; // 选择位置状态下的互动信息
overlay_materials?: CardMeta[]; // 超量素材
counters: { [type: number]: number }; // 指示器
reload?: boolean; // 这个字段会在收到MSG_RELOAD_FIELD的时候设置成true,在收到MSG_UPDATE_DATE的时候设置成false
}
......@@ -328,6 +329,7 @@ export function updateCardData<T extends DuelFieldState>(
if (payload.defense !== undefined && payload.defense >= 0) {
occupant.data.def = payload.defense;
}
// TODO: counters
}
if (target?.reload) {
target.reload = false;
......@@ -351,6 +353,7 @@ export function reloadFieldMeta<T extends DuelFieldState>(
position: action.position,
},
idleInteractivities: [],
counters: {},
reload: true,
};
});
......
......@@ -93,6 +93,7 @@ export const handsCase = (builder: ActionReducerMapBuilder<DuelState>) => {
controler: player,
location: ygopro.CardZone.HAND,
},
counters: {},
idleInteractivities: [],
};
});
......@@ -130,6 +131,7 @@ export const handsCase = (builder: ActionReducerMapBuilder<DuelState>) => {
occupant: { id: code, data: {}, text: {} },
location: { controler },
idleInteractivities: [],
counters: {},
});
});
builder.addCase(insertHandMeta.fulfilled, (state, action) => {
......@@ -154,6 +156,7 @@ export const handsCase = (builder: ActionReducerMapBuilder<DuelState>) => {
location: ygopro.CardZone.HAND,
},
idleInteractivities: [],
counters: {},
};
});
......
......@@ -37,6 +37,7 @@ export const initMagicsImpl: CaseReducer<DuelState, PayloadAction<number>> = (
sequence: 0,
},
idleInteractivities: [],
counters: {},
},
{
location: {
......@@ -45,6 +46,7 @@ export const initMagicsImpl: CaseReducer<DuelState, PayloadAction<number>> = (
sequence: 1,
},
idleInteractivities: [],
counters: {},
},
{
location: {
......@@ -53,6 +55,7 @@ export const initMagicsImpl: CaseReducer<DuelState, PayloadAction<number>> = (
sequence: 2,
},
idleInteractivities: [],
counters: {},
},
{
location: {
......@@ -61,6 +64,7 @@ export const initMagicsImpl: CaseReducer<DuelState, PayloadAction<number>> = (
sequence: 3,
},
idleInteractivities: [],
counters: {},
},
{
location: {
......@@ -69,6 +73,7 @@ export const initMagicsImpl: CaseReducer<DuelState, PayloadAction<number>> = (
sequence: 4,
},
idleInteractivities: [],
counters: {},
},
{
// 场地区
......@@ -78,6 +83,7 @@ export const initMagicsImpl: CaseReducer<DuelState, PayloadAction<number>> = (
sequence: 5,
},
idleInteractivities: [],
counters: {},
},
],
};
......
......@@ -61,6 +61,7 @@ import {
resetCheckCardModalV3Impl,
setCheckCardModalV3SelectedImpl,
checkCardModalV3Case,
setCardModalCountersImpl,
} from "./modal/mod";
import {
MonsterState,
......@@ -72,6 +73,7 @@ import {
removeMonsterImpl,
setMonsterPositionImpl,
removeOverlayImpl,
updateMonsterCountersImpl,
monsterCase,
} from "./monstersSlice";
import {
......@@ -162,7 +164,7 @@ export interface DuelState {
const initialState: DuelState = {
modalState: {
cardModal: { isOpen: false, interactivies: [] },
cardModal: { isOpen: false, interactivies: [], counters: {} },
cardListModal: { isOpen: false, list: [] },
checkCardModal: { isOpen: false, cancelAble: false, tags: [] },
yesNoModal: { isOpen: false },
......@@ -213,6 +215,7 @@ const duelSlice = createSlice({
setMonsterPosition: setMonsterPositionImpl,
removeMonster: removeMonsterImpl,
removeOverlay: removeOverlayImpl,
updateMonsterCounters: updateMonsterCountersImpl,
// 魔法陷阱区相关`Reducer`
initMagics: initMagicsImpl,
......@@ -277,6 +280,7 @@ const duelSlice = createSlice({
setCheckCardModalV3ResponseAble: setCheckCardModalV3ResponseAbleImpl,
resetCheckCardModalV3: resetCheckCardModalV3Impl,
setCheckCardModalV3Selected: setCheckCardModalV3SelectedImpl,
setCardModalCounters: setCardModalCountersImpl,
// 通用的`Reducer`
clearAllIdleInteractivities: clearAllIdleInteractivitiesImpl,
......@@ -337,6 +341,7 @@ export const {
clearMonsterIdleInteractivities,
setMonsterPosition,
removeMonster,
updateMonsterCounters,
removeOverlay,
initMagics,
addMagicPlaceInteractivities,
......@@ -389,6 +394,7 @@ export const {
setCheckCardModalV3ResponseAble,
resetCheckCardModalV3,
setCheckCardModalV3Selected,
setCardModalCounters,
} = duelSlice.actions;
export const selectDuelHsStart = (state: RootState) => {
return state.duel.meInitInfo != null;
......
......@@ -27,9 +27,19 @@ export const setCardModalInteractiviesImpl: CaseReducer<
state.modalState.cardModal.interactivies = action.payload;
};
// 更新卡牌弹窗指示器
export const setCardModalCountersImpl: CaseReducer<
DuelState,
PayloadAction<{ [type: number]: number }>
> = (state, action) => {
state.modalState.cardModal.counters = action.payload;
};
export const selectCardModalIsOpen = (state: RootState) =>
state.duel.modalState.cardModal.isOpen;
export const selectCardModalMeta = (state: RootState) =>
state.duel.modalState.cardModal.meta;
export const selectCardModalInteractivies = (state: RootState) =>
state.duel.modalState.cardModal.interactivies;
export const selectCardModalCounters = (state: RootState) =>
state.duel.modalState.cardModal.counters;
......@@ -7,6 +7,7 @@ export interface ModalState {
isOpen: boolean;
meta?: CardMeta;
interactivies: { desc: string; response: number }[];
counters: { [type: number]: number };
};
// 卡牌列表弹窗
cardListModal: {
......
......@@ -22,6 +22,9 @@ import {
removeOverlay,
} from "./generic";
import { fetchCard } from "../../api/cards";
type MsgUpdateCounter = ReturnType<
typeof ygopro.StocGameMessage.MsgUpdateCounter.prototype.toObject
>;
export interface MonsterState extends DuelFieldState {}
......@@ -40,6 +43,7 @@ export const initMonstersImpl: CaseReducer<DuelState, PayloadAction<number>> = (
sequence: 0,
},
idleInteractivities: [],
counters: {},
},
{
location: {
......@@ -48,6 +52,7 @@ export const initMonstersImpl: CaseReducer<DuelState, PayloadAction<number>> = (
sequence: 1,
},
idleInteractivities: [],
counters: {},
},
{
location: {
......@@ -56,6 +61,7 @@ export const initMonstersImpl: CaseReducer<DuelState, PayloadAction<number>> = (
sequence: 2,
},
idleInteractivities: [],
counters: {},
},
{
location: {
......@@ -64,6 +70,7 @@ export const initMonstersImpl: CaseReducer<DuelState, PayloadAction<number>> = (
sequence: 3,
},
idleInteractivities: [],
counters: {},
},
{
location: {
......@@ -72,6 +79,7 @@ export const initMonstersImpl: CaseReducer<DuelState, PayloadAction<number>> = (
sequence: 4,
},
idleInteractivities: [],
counters: {},
},
{
location: {
......@@ -80,6 +88,7 @@ export const initMonstersImpl: CaseReducer<DuelState, PayloadAction<number>> = (
sequence: 5,
},
idleInteractivities: [],
counters: {},
},
{
location: {
......@@ -88,6 +97,7 @@ export const initMonstersImpl: CaseReducer<DuelState, PayloadAction<number>> = (
sequence: 6,
},
idleInteractivities: [],
counters: {},
},
],
};
......@@ -148,6 +158,44 @@ export const addMonsterIdleInteractivitiesImpl: CaseReducer<
);
};
export const updateMonsterCountersImpl: CaseReducer<
DuelState,
PayloadAction<MsgUpdateCounter>
> = (state, action) => {
const monsters = judgeSelf(action.payload.location?.controler!, state)
? state.meMonsters
: state.opMonsters;
if (monsters) {
const target = monsters.inner.find(
(_, idx) => idx == action.payload.location?.sequence!
);
if (target) {
const count = action.payload.count!;
const counterType = action.payload.action_type!;
switch (action.payload.action_type!) {
case ygopro.StocGameMessage.MsgUpdateCounter.ActionType.ADD: {
if (counterType in target.counters) {
target.counters[counterType] += count;
} else {
target.counters[counterType] = count;
}
break;
}
case ygopro.StocGameMessage.MsgUpdateCounter.ActionType.REMOVE: {
if (counterType in target.counters) {
target.counters[counterType] -= count;
}
break;
}
default: {
break;
}
}
}
}
};
export const clearMonsterIdleInteractivitiesImpl: CaseReducer<
DuelState,
PayloadAction<number>
......
......@@ -26,6 +26,7 @@ import onMsgUpdateData from "./updateData";
import onMsgReloadField from "./reloadField";
import onMsgSelectSum from "./selectSum";
import onMsgSelectTribute from "./selectTribute";
import onMsgUpdateCounter from "./updateCounter";
import { setWaiting } from "../../reducers/duel/mod";
const ActiveList = [
......@@ -175,6 +176,11 @@ export default function handleGameMsg(pb: ygopro.YgoStocMsg) {
break;
}
case "update_counter": {
onMsgUpdateCounter(msg.update_counter, dispatch);
break;
}
case "unimplemented": {
onUnimplemented(msg.unimplemented, dispatch);
......
import { ygopro } from "../../api/ocgcore/idl/ocgcore";
import { updateMonsterCounters } from "../../reducers/duel/mod";
import { AppDispatch } from "../../store";
import MsgUpdateCounter = ygopro.StocGameMessage.MsgUpdateCounter;
export default (updateCounter: MsgUpdateCounter, dispatch: AppDispatch) => {
dispatch(updateMonsterCounters(updateCounter.toObject()));
};
......@@ -5,6 +5,7 @@ import {
selectCardModalIsOpen,
selectCardModalInteractivies,
selectCardModalMeta,
selectCardModalCounters,
} from "../../reducers/duel/modal/mod";
import {
setCardModalIsOpen,
......@@ -39,6 +40,7 @@ const CardModal = () => {
const desc = meta?.text.desc;
const atk = meta?.data.atk;
const def = meta?.data.def;
const counters = useAppSelector(selectCardModalCounters);
const imgUrl = meta?.id
? `${NeosConfig.cardImgUrl}/${meta.id}.jpg`
: undefined;
......@@ -66,6 +68,9 @@ const CardModal = () => {
<p>
<AtkLine level={level} atk={atk} def={def} />
</p>
<p>
<CounterLine counters={counters} />
</p>
<p>{desc}</p>
</Card>
{interactivies.map((interactive, idx) => {
......@@ -141,4 +146,25 @@ const AttLine = (props: {
);
};
const CounterLine = (props: { counters: { [type: number]: number } }) => {
const counters = [];
for (const counterType in props.counters) {
const count = props.counters[counterType];
if (count > 0) {
const counterStr = fetchStrings("!counter", `0x${counterType}`);
counters.push(`${counterStr}: ${count}`);
}
}
return counters.length > 0 ? (
<Row gutter={8}>
{counters.map((counter) => (
<Col>{counter}</Col>
))}
</Row>
) : (
<></>
);
};
export default CardModal;
......@@ -8,6 +8,7 @@ import { ygopro } from "../../api/ocgcore/idl/ocgcore";
import {
setCardListModalInfo,
setCardListModalIsOpen,
setCardModalCounters,
setCardModalInteractivies,
setCardModalIsOpen,
setCardModalMeta,
......@@ -68,6 +69,7 @@ const FixedSlot = (props: {
})
)
);
dispatch(setCardModalCounters(props.state.counters));
dispatch(setCardModalIsOpen(true));
// 侧边栏展示超量素材信息
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment