feat: 14 more in-game test scenarios, quests included (#632, part 2) - #87
Merged
Merged
Conversation
A scenario can now click an NPC or a player the way a player does: `nearby` gives each player, monster and NPC a `pixel` at the middle of the box the mouse picks it by, or null while it is not drawn. The projection uses the world camera as it is when the mouse ray is cast (Frames::RecordWorldCamera in MainScene), because later in the frame other cameras draw the item previews; g_Camera at the time of `state` put every character at the same depth. Clicking the pixel of the vault keeper in Lorencia opens the vault.
The editor drops the frame's Enter and mouse buttons while ImGui wants the mouse, and the left button while the real pointer rests on the "Open Editor" button; the chat box does not open on Enter then either. ImGui follows the real pointer, so a player's mouse over a test client's window could swallow a key or click the control socket injected. Those never went through ImGui: the checks now let them pass (Synthetic::IsIdle, a constant true without the control socket).
- `chat` (Player Interactions): two players meet in Lorencia; one opens the chat box with Enter, types a line and sends it with Enter, the other answers the same way, and both lines show in both chat logs. - The walking and trading steps of the trade scenario moved into Meeting and Trading, for the scenarios to come; GameClient has ZenAsync and CountAsync. - Keys.PressUntilAsync presses a key until its effect shows, at most three times: an injected Enter was lost now and then on the second client (the cause is not known yet), and each extra press is noted in the log. - `ui` also reports chat_input, party, command, my_shop and purchase_shop.
- `npc-shop` (new category NPC Interactions): test7Dk walks up to Hanzo the Blacksmith and talks to him with a click on him; drags a potion stack onto his shop (sold at once) and a Jewel of Bless (a dialog asks, Enter confirms), each for the selling price its tooltip shows; then buys the cheapest of his goods that fits, for its price. - slot-pixel grid `npc_shop`; `ui` elements inventory.repair, inventory.my_shop, npc_shop.repair and npc_shop.repair_all while shown. - state: `npc_shop` (repair_shop, tax_rate, goods with their price, tax included), `repair_mode`, `max_durability` per item, and `sell_price` per inventory item while an NPC shop is open. - CNewUIMyInventory has one check for when its repair and shop buttons show (render and click used two copies); CNewUINPCShop exposes its grid and repair buttons. - Npcs (walk up to an NPC, talk with a click on its pixel) and ItemSlots.FreeArea, shared with the Icarus scenario.
- `repair` (NPC Interactions): test9Dk opens the inventory, presses its repair button and clicks the worn gloves; then talks to Hanzo the Blacksmith with a click and presses Repair all for the boots. Each repair brings the durability back to its maximum and costs zen; what it cost and what the client showed go into the log. - state: `repair_price` on each worn item while a click would repair it (the tooltip's own ConvertRepairGold), and npc_shop.repair_all_price. The client shows a higher price than OpenMU 0.9.10 charges (gloves 1,700 shown, 1,400 charged; Repair all 720 shown, 620 charged): CalcRepairCost uses 3.5 * sqrt(base) * sqrt(sqrt(base)) where OpenMU's ItemPriceCalculator.CalculateRepairPrice uses 3.0.
testgm2Sum wears something in all twelve equipment slots. Slot by slot, the item is dragged off onto a free area of the inventory with two clicks and put back on with a right-click on it; each move is checked in the equipment and the inventory.
- `party` (Player Interactions): five clients. The leader opens the command window with D; for each of four players it clicks Party and right-clicks the player where its client draws it, and the player accepts the invitation with Enter. After each join the party lists of both show the new size with the leader first; at the end all five show the same party of five. - `party` events: `invited` (with the inviter's name) and `result` for the server's answer to an invitation that formed no party (denied, full, other_party, ...). - `ui` elements command.trade, command.purchase and command.party while the command window is open. - A step can name the clients it pictures, so five clients do not put five screenshots into every step; a failed step pictures all of them. - Meeting.PixelOfAsync finds where a client draws a player or an NPC.
test7Dw and test8Dw have no free 2x2 area. Three trades that cannot complete: 1. the first offers a 2x2 item the second has no room for; 2. the second offers one the first has no room for; 3. both: the first offers a 2x2 item, the second more single items than the first then has free squares, taken on alternate squares so that no 2x2 area of its own frees up. Each time both confirm, the trade closes for both with "inventory full", and both keep the same items in the same slots and the same zen.
- `personal-shop` (Player Interactions): quest1Dk opens the inventory, presses its shop button, puts a Jewel of Bless into the shop with two clicks, types its price into the dialog, types the shop's title and opens the shop (a dialog asks, Enter confirms). quest2Dk opens it through the command window (D, Buy, a right-click on the seller), clicks the jewel and confirms with Enter: the jewel and 9,000,000 zen change owner and the seller's shop is empty. - state: `my_shop` (open, title, goods with prices) and `purchase_shop` (seller, title, goods with prices, or null); slot-pixel grids `my_shop` and `purchase_shop` (slots 204 and up, as the server numbers them); `ui` elements my_shop.title, my_shop.open and my_shop.close.
- in-game-tests.md: all nine scenarios with their categories and test accounts, why test items start worn, the shared helpers, what is not covered yet. - control-socket.md: the new `ui` elements, `slot-pixel` grids, state fields (shops, repair and sell prices, max_durability, repair_mode), party events, and that `type` enters text. - Steps where only one player acts picture only that player: 20 fewer screenshots in a run of all scenarios.
…s more - The list takes the window's free height, and each test is one line (the description in full on hover); the log sits below a divider that can be dragged. Eight tests and the three categories fit at once in the default window, where two did before. - A run no longer disables the list: it scrolls and folds as before, only the checkboxes and the wait and quality fields are locked. - The running test's row is highlighted, its category opens and the row scrolls into view when it starts; a "Now" line under the overall bar shows the test and its current step. - "Open report folder" also works during a run.
The quest scenarios to come change the quest Dark Knights' class; the Dark Lords take no class change quest, stand on the same tile in Devias and have the same zen and jewels.
Seven scenarios in the new category Quests, all through clicks on the NPC and on the answers of its quest dialog: - quest-blade-knight, quest-soul-master, quest-muse-elf: Sebina's "Find the Scroll of the Emperor" and "Three Treasures of MU" turn a Dark Knight, Dark Wizard and Elf into their second class (+10 level-up points each); the Blade Knight then spends a point on strength with the character window's "+". - quest-hero-status-wizard, quest-hero-status-elf: a level 400 Soul Master and Muse Elf find Marlon (he wanders between Devias, Lorencia, Noria and Atlans) and gain hero status: one more point per level, which the server pays for the 180 levels above 220 at once; the Muse Elf learns Infinity Arrow. - quest-combo: a Blade Knight gains hero status and Marlon's combo, which is still there after logging in again. - quest-evidence-of-strength: Apostle Devin's first quest towards the third class (+20 points). Control socket: - `ui`: window npc_quest; elements npc_quest.answer.N (the rows its answers are clicked in, as CNewUINPCQuest hits them), npc_quest.complete, npc_quest.close, and the character window's character.stat.* buttons. - state: class_name, level_up_points, stats, combo, the seven legacy `quests` with their state, and `npc_quest` (quest, page, text, need_zen, answers with what a click on each does). - `quest` events: a quest's new state (decoded by the client from A1/A2) and each reward of A3.
Marlon wanders between Devias, Lorencia, Noria and Atlans, so the quest scenarios looked on all four spots, about a minute each time. The test server now logs his moves: docker-compose.yml sets OpenMU's WanderingMerchantsPlugIn to Debug and puts the log properties, with the game server's scope, into each line. The tester reads the container's log (TestServer.WanderingNpcAsync) for his last spawn on game server 0, the one behind the published port, warps to that map and walks up to him: 13 to 30 s instead of 45 to 70 s. A freshly started server places him some 20 s after start, so the tester waits up to 30 s for that line; on another server, or when he is not where the log says, it searches as before.
Every category header in the window (Player Interactions, NPC Interactions, Game Behaviour, Quests) has a Check all / Uncheck all button for its tests, next to the one for all tests; it follows the category's checkboxes, is locked during a run, and a click on it does not fold the category.
Mosch0512
commented
Sep 29, 2026
Mosch0512
left a comment
Owner
Author
There was a problem hiding this comment.
Review of 67e2094: 15 points, each in an inline comment. Nothing was run for this review; each point comes from reading the code.
Bugs, most serious first:
MainWindow.axaml: the log divider resizes the Run bar, not the list or the log.PersonalShopScenario.cs: a slow server makes the price step press Enter again, which opens the chat box.RepairScenario.cs: the equipment is read right after login, before it may have arrived.Quests.cs: the reward wait doesn't check whose reward it is.ControlState.cpp:repair_priceis reported for an item value of -1, where the tooltip shows no price.ControlTaps.cpp: theamountof the class-change rewards is the server's class code.ControlCommandsUi.cpp: the unknown-grid error leaves out the shop grids.ControlState.cpp: one quest state has two names,noneinstateandunknownin the events.MainWindow.axaml.cs: a run saves folded categories as unfolded.
Cleanup: the Marlon lookup rereads the whole container log; three duplications (the pixel projection, the grid fit, the stat-button count); the checks for injected input in three places; QuestScenarios.cs holds six classes.
Bugs: - The window's divider sat between the Run bar and the log, so it resized the bar; the Run bar moved above the list, and the divider is between the list and the log. - personal-shop pressed Enter again when the server was slow to put the priced jewel into the shop, which opened the chat box: the step now presses until the dialog closes and waits for the price. - repair read the equipment right after login; it waits for it now. - The quest reward wait matches the character's own name: the client records the rewards of every player in view. - `repair_price` is left out where ItemValue is -1 (a Dark Lord pet without its data), as the tooltip shows no price then. - The class-change rewards carry no `amount`: the packet's number is the server's class code; the new class is in `class`. - The unknown-grid error names the shop grids too. - A quest state outside the known ones reads `unknown` in `state` as in the events. - A run no longer saves the categories it unfolded to show its test as unfolded. - The Marlon lookup reads only the last 20,000 log lines and waits for his line only while the container is younger than a minute; an older test server without the line (set up before its compose file logged it) gets no 30 s wait. Cleanup: - CameraProjection::WorldToWindowPixel is the projection `nearby` uses, next to the mouse ray it mirrors. - ItemSlots.FreeArea takes any grid, for the trade window and the NPC shop. - CNewUICharacterInfoWindow::StatButtonCount is the one count of the "+" buttons. - Synthetic::IsInjecting is the one test the editor's input checks use. - One file per quest scenario.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part 2 of sven-n#632: seven more in-game test scenarios, seven quest scenarios, and a tester window that stays usable during a run. Builds on part 1 (fork PR #79, merged into the fork's main; upstream sven-n#652).
Scenarios
chatpartypersonal-shoptrade-inventory-fullnpc-shoprepairequip-all-slotsQuest scenarios (new category Quests)
All go through clicks on the NPC and on the answers of its quest dialog. The quest accounts of OpenMU's test data (first class, 100,000,000 zen, quest items prepared) stand next to Sebina and Devin in Devias.
quest-blade-knightquest-soul-masterquest-muse-elfquest-hero-status-wizardquest-hero-status-elfquest-comboquest-evidence-of-strengthui: windownpc_quest; elementsnpc_quest.answer.N,npc_quest.complete,npc_quest.closeandcharacter.stat.*.state:class_name,level_up_points,stats,combo,quests, andnpc_quest(the page, its text, and each answer with what a click on it does).quest(a quest's new state, and each reward).personal-shopnow uses the quest accounts' Dark Lords, because the quest scenarios change the Dark Knights' class.Tester window
Control socket
nearby→pixel: where a player or NPC is drawn, projected with the world camera kept from the frame that casts the mouse ray. Theg_Camerathatstatesees is the item-preview camera.click-uion that pixel talks to an NPC or targets a player.ui:chat_input,party,command,my_shop,purchase_shop;inventory.repair,inventory.my_shop,npc_shop.repair,npc_shop.repair_all,my_shop.title,my_shop.open,my_shop.close,command.trade,command.purchase,command.party.slot-pixel: gridsnpc_shop,my_shop,purchase_shop.state:max_durability,repair_price(while a click would repair), andsell_price(while an NPC shop is open), as the tooltips show them;npc_shop,my_shop,purchase_shop,repair_mode.partyinvited, andpartyresultfor an invitation that formed no party.Client changes outside the socket
CNewUIMyInventory: one check for when its repair and shop buttons show, instead of two copies.Tester
Meeting(walk to a spot or to a player, find where a character is drawn),Trading,Npcs(walk up to an NPC, talk with a click),Keys.PressUntilAsync,ItemSlots.FreeArea.Keys.PressUntilAsyncpresses a key again when nothing happened within 2 s. That's safe because a handled key shows its effect at once. Each extra press is noted in the log.Findings
CalcRepairCostuses a factor of 3.5 whereItemPriceCalculator.CalculateRepairPriceuses 3.0. The test logs both and doesn't fail on it.Tested
ctest(Debug) 339/339.