Repository navigation
Add the Duel Arena minigame - #1274
HarleyGilpin wants to merge 19 commits into
Conversation
| val opponent = duel.opponent(player) | ||
| if (duel.hasRule("fun_weapons") && !DuelRules.hasFunWeapon(player)) { | ||
| player.message("Fun Weapons is selected but you don't have a 'fun weapon'.") | ||
| opponent.message("Fun Weapons is selected but your opponent does not have a 'fun weapon'.") |
There was a problem hiding this comment.
This should go to the player not the opponent
There was a problem hiding this comment.
Done in b2232c0b0. canAccept now takes the accepting player and only messages them: "you don't have" for their own missing weapon, "your opponent does not have" for the other side. The opponent hears nothing until they click Accept themselves.
| } | ||
| if (needed > player.inventory.spaces) { | ||
| player.message("You do not have enough space for the items removed and/or the stake.") | ||
| opponent.message("Your opponent does not have enough space for the items removed and/or the stake.") |
There was a problem hiding this comment.
| opponent.message("Your opponent does not have enough space for the items removed and/or the stake.") | |
| player.message("Your opponent does not have enough space for the items removed and/or the stake.") |
There was a problem hiding this comment.
Done in b2232c0b0, same shape as the fun weapon check: both messages go to the player who clicked Accept. Test updated so the opponent's copy only appears once they press Accept too.
| player.interfaces.sendText(screen, "stake", itemsText(player.stake)) | ||
| player.interfaces.sendText(screen, "other_stake", itemsText(player.otherStake)) |
There was a problem hiding this comment.
Right, cs2 206 fills both stake texts on 626 from the offer inventories itself. Removed both sendText calls and the now-unused itemsText in f625a0fc0.
| for (rule in DuelRules.all) { | ||
| player.clear("duel_$rule") | ||
| } | ||
| player.attackers.clear() |
There was a problem hiding this comment.
Hits need clearing as well.
If you shoot a killing blow with a bow over distance (aka against a low hp opponent), then quickly forfeit. You lose but the player get's hit after being teleported back to the arena, so they will end up losing their items and being sent to lumbridge.
There was a problem hiding this comment.
Good catch. cleanup now clears each player's hit queue in 8f7b83476, so an arrow released before the forfeit never lands after the teleport. Added a test that queues a lethal ranged hit, forfeits through the trapdoor and checks the winner is alive at full HP in the hospital; it fails without the clear.
| if (inventories.contains("duelwinnings")) { | ||
| returnItems(winnings) | ||
| } | ||
| save(this) |
There was a problem hiding this comment.
This is resaving every player every-time they log in, rather than only when things are returned?
There was a problem hiding this comment.
Yes it was. Fixed in 1364ff9: playerSpawn now only queues a save when a leftover stake, mirrored stake or winnings actually had contents and were moved. A normal login touches nothing and doesn't save.
| true | ||
| } | ||
|
|
||
| objectOperate("Forfeit", "duel_arena_forfeit_trapdoor", arrive = false) { |
There was a problem hiding this comment.
How does one forfeit when movement is disabled?
There was a problem hiding this comment.
Good point, it couldn't. The trapdoor was an objectOperate and with movement_delay running the interaction just waited forever.
On RS the doors could be clicked from anywhere in a No Movement duel (wiki: "If 'no movement' was enabled, the doors could simply be clicked to bring up the forfeit chatbox"). Done in 7343eb6: the trapdoor is now an objectApproach. In a No Movement duel the forfeit prompt opens from where the player stands, otherwise approachRange(-1) makes them walk up to it as before. Arenas have two doors on opposite ends of a ~15x11 area so the nearest one is always inside the default approach range. Tests cover both paths.
There was a problem hiding this comment.
Follow-up in 1aa6034: the map trapdoors sit just outside the 17-wide arenas, so a frozen player can be 12 tiles away and the approach never launched within the default 10. objectApproach now takes an optional range that PlayerOnObjectInteract uses as its starting approach range; the trapdoor registers 16. Test moved to 12 tiles to cover it.
8f7b834 to
50f3f65
Compare
Component ids and the rule varbits (4157-4183, 4275 on varp 286) were dumped from the 634 cache; Darkan's rule bit encoding and most of its 727 component ids don't match this revision. teleport_block was declared on varbit 4163, which is the duel arena's No Magic rule in 634, so it becomes a plain persisted variable.
Challenge requests, friendly and staked rules screens with all rules and equipment bans, mirrored stakes, confirmation screens, arena placement (obstacle/no-obstacle, no-movement side-by-side), countdown, rule enforcement, forfeit trapdoor, logout veto with forfeit on disconnect, victory screens with winnings, hospital healing and an in-memory scoreboard of the last fifty duels. Duellers and their familiars can only fight their opponent. Food, drink, prayer, summoning, movement and equipment bans are enforced through generic message/slot variables read by the existing skill scripts.
Clicking a duel request in the chat box makes the client send player option 1, so the option has to live there like Attack does.
Interface 632 rebuilds its rows from varc strings 224-273 when it opens and shows Loading... while all of them are blank, so the rows are sent as those strings before opening instead of appended with a row script.
The victory screens copy that string over the name component when they open, which replaced the text sent directly with null.
Varc 236 turns the worn equipment tab icon into the forfeit trapdoor during a duel; the 634 cache has no forfeit tab interface for any other slot, so clicking that tab now asks to forfeit like the trapdoor does.
Drop the equipment tab icon swap; the logout button now asks whether to forfeit instead of refusing, and the trapdoor keeps working.
Queue a save of both players whenever stake items change hands so a crash can't restore a pre-transfer snapshot, return leftover stakes instead of clearing them, route the bank fallback through BankDeposit so noted and unbankable items are handled, re-check inventory space at the final accept, and treat every death inside an arena as drop-free so simultaneous deaths never drop items.
50f3f65 to
64a72f7
Compare
With the No Movement rule the player is frozen so they could never walk to the trapdoor and the click silently waited forever. The trapdoor is now an approach interaction: in a No Movement duel the forfeit prompt opens from wherever the player stands, otherwise they walk up to it as before. Arenas are two doors on opposite ends of a ~15x11 area, so the nearest door is always inside the default approach range.
Approach handlers only launch once the player is within the default ten tiles, so a frozen player further away than that just waited forever. objectApproach now takes an optional range which PlayerOnObjectInteract uses as the starting approach range. The duel trapdoors sit outside the 17 wide arenas, up to 12 tiles from a No Movement player, so they use 16.
GregHib
left a comment
There was a problem hiding this comment.
The spoils screen also combines the stake reward and the original amount staked, not sure if that's correct. E.g. staking 3k against 100k and winning will show the spoils as 103k which is not what you won.
| @@ -0,0 +1,7 @@ | |||
| [duel_arena_forfeit_trapdoor] | |||
| id = 3203 | |||
There was a problem hiding this comment.
There are two more forfeit objects, 3111 & 3113


Adds the Duel Arena -
content/minigame/duel_arenaWhat's included
Generic hooks were added to
Eating,Equipping,Movement,SummoningandQuickPrayers(no_food_message,blocked_equip_slots, ...) because item/interface option handlers can't veto each other.Tests
DuelTest(21 WorldTest cases) covers the request handshake, rule toggles and exclusions, stake mirroring/removal/restrictions, accept space and fun-weapon checks, countdown and fight start, death and forfeit payouts, disconnect forfeit, logout veto, walk-away decline, third-party attack blocking, leftover-item recovery and the scoreboard cap. Full:game:testpasses.