-
Notifications
You must be signed in to change notification settings - Fork 395
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[RFC] Use maps instead of strings for messages #7971
base: master
Are you sure you want to change the base?
Conversation
This is an insane amount of work. I'm quite busy this weekend but will review on Monday! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
small things to think about
@@ -1225,7 +1224,7 @@ | |||
state side eid | |||
{:msg (->> payment-result | |||
(keep :paid/msg) | |||
(enumerate-str)) | |||
(apply merge {})) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will overwrite, which you don't want.
(assert (= catch 'catch)) | ||
(assert (symbol? sym)) | ||
(if (cljs-env? &env) | ||
`(try ~@try-body (~'catch js/Object ~sym ~@catch-body)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
`(try ~@try-body (~'catch js/Object ~sym ~@catch-body)) | |
`(try ~@try-body (~'catch :default ~sym ~@catch-body)) |
;; This is inverted -- corp forcing effect means it's forcing runner to take the effect. | ||
(if (= (keyword side) :corp) "Runner" "Corp") | ||
" to " | ||
;; oh god |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lmao
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just as another note, in the current branch I've added an optional :display-side
key that can be used to set the msg
source without changing the side of the ability itself, and you can also just do :msg :cost
to get something like player pays [cost] to satisfy [card]
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, that's a good call-out. It might be possible to pass down both the side and display-side and then use that to figure out if "force" wording should be applied. It looks like only a few cards use this right now (Wildcat Strike, Public Trail), from which it seems something like (when (and (= display-side :runner) (= side :corp)) <add force>)
would work. I'll check some more cards to see if that logic holds up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that's the current usage because it's something I've ported over from playtest, but it will be more relevant for future cards (ie dawn/elevation, and the set coming after it). Anything which uses cost-option
as part of the choose-one-helper
will use this functionality.
For reference, wildcat strike would be force the runner to x
, and public trail would be pays x to satisfy y
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Public Trail (and other cards that would use "satisfy" wording) are easy enough to deal with. For Wildcat Strike, I came up with some code to be able to handle it, but looking into Funhouse, it doesn't seem like the logic translates well.
Funhouse (corp card, corp display side, runner selection) encounter effect:
- force the runner to take 1 tag (Runner effect) on encountering it
- end the run (Corp effect)
and its subroutine (corp card, corp display side, runner selection):
- force the runner to pay 4c (Runner effect)
- give the runner 1 tag (Corp effect)
I can't figure out a way to make display-side work with this nicely, since in theory the display-side would need to be for each individual effect, not the overall ability. I'd like to know your thoughts on how this should (or should not) extend to ice abilities. In essence, I need a way to indicate that it's the other side paying for it, which is why I currently have this <effect>-force
keyword logic (which I don't like, for the record).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm - so if the ability is formatted specifically as {:cost ... :msg :cost}, then regardless of side, it's (active player) pays (cost) to satisfy (card)
. Is that helpful?
In any case, funhouse is done completely manually, and is very verbose, so that could simply be rewritten. I'll look at rewriting it a little later if you like.
This is the first phase of moving text rendering for log messages to the client. The :text field now returns a map, which contains the username (depending on the action) and raw-text, which contains the text without the default "<username> <text>." rendering. The client side in this change now takes care of the rendering of the username (if present) and raw text. Text generation will incrementally move into additional fields in this map, leading to an incremental process where more and more logic can be moved to the client.
The tests rely on the raw log state, which is now maps instead of strings. To maintain compatability, all such functions now call render-map to get the string that would otherwise be displayed.
currently dropping the sequential nature furthermore, this uses merge, so it won't add credits and other stuff tset pays 2 from Overclock and trashes Self-modifying Code to use Self-modifying Code to install a program from the stack. tset pays 3 from Overclock and trashes Self-modifying Code to use Self-modifying Code to install Unity from the Stack. this is example of redundancy that could be used to justify the split cost thing more example output after the change: tset trashes Simulchip, trashes 1 installed program (Gauss), and pays 0 to use Simulchip to install Gauss from the Heap (paying 3 less). current output: tset trashes Simulchip and trashes 1 installed program (Self-modifying Code), and then pays 0 , to use Simulchip to install Fermenter from the Heap (paying 3 less). need to fix this properly
This is the equivalent of msg, except the output is a map instead of a string.
pronoun subsitution isn't working quite right in test mode, still need to figure that out
i think it's some missing local config since the test is technically passing but the lack of 'is' is making is ad
Just getting the rebase out of the way. No significant changes yet. |
@@ -435,15 +435,10 @@ | |||
:else | |||
(name-zone :runner (:previous-zone card)))) | |||
"") | |||
pre-lhs (when (every? (complement empty?) [cost-str prepend-cost-str]) | |||
(str prepend-cost-str ", and then ")) | |||
;; currently loses ", and then" -- all costs are squashed into one |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fixable, but I want to check if people actually like the way this is. I'm sure there's a good reason for it, but it seems a bit contrived to do it this way instead of having two separate messages.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you give an example? what's wrong with the current message?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So there are two reasons for this:
- I wanted to reduce clutter in the log. This is relevant for quite a few cards (ie SMC, tucana) - these used to be like 5 lines for one effect, now they're one concise and readable line (they're still technically two checkpoints, but there's maybe one card in the game that cares about that)
- I wanted to make it so we aren't rewriting the same display code with every install. So you simply do (install ... {:msg-keys {...}}) and let that handle the formatting. Sooner or later I'm going to add this system to rezzing, and a few other areas too. (currently, if an effect rezzes a card, you will get several lines, one for the cost, one for the discount, one for the card being rezzed, and one for whatever the ability map does - when there should just be one)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This felt a bit complicated from the code POV, but if the reason is to reduce the number of logs, then that makes perfect sense. I'll fix this up so costs can be sequenced -- something like cost being a vector of map/vectors instead of a single map.
This is a draft change for moving the message system to use maps instead of strings. The main intent is to enable localization of messages in the front-end by transforming those maps into strings, with some other benefits such as consistent templating for all messages. This is not a complete change -- I'm posting this to get some feedback on the approach and figure out what adaptions are needed in order to move forward with this change. (Please note the changes
are on a ~6 month old branch and will be rebased later.)
Today, the message that gets passed to the client is:
{:username <username> :text <string>}
which has been changed to:
{:username <username> :side <:corp/:runner> :type <TYPE> :cost {COST ...} :effect {EFFECT ...} <other-args ...> (:raw-text <string>)}
This is turned into a string in the user's language via
render-map
.Type is more or less the main verb for the sentence ("use", "install", "rez", "steal", etc.), cost is self-explanatory, and effect is the result (i.e. anything that's not a cost). There's plenty of examples in
test/cljc/i18n/en_test.clj
(en: Add test for rendering).The changes are:
render-map
based on the language to get the string say: Return text as a maprender-map
for English en: render-map supportI have a couple other changes (
render-map
for Japanese, more updates for NSG cards, etc.) but I'm excluding these for now to focus on the core changes.As part of working through effects, I was poking around to see if it was possible to have effects generate messages instead of having to specify a :msg for every effect. More or less every effect should have a message (and we could just mark the ones that shouldn't as silent), so in theory it should be possible to coalesce and pass down effects -- instead of what you see here, which is more or less updating every single :msg in cards to follow an effect format. I haven't been able to figure out if it's actually practical to do this, so I've just been updating cards with the new format for messages.
This change is also backwards compatible. With the current approach, generated messages are still the same even if
:msg
hasn't been updated to the new format. If a string in{:msg ability}
ends up atprint-msg
, it just stuffs that into:raw-text
which is later appended to the transformed string with the type and cost. The only exception I've found so far is that some messages incorporate the cost (which is now a map) into:msg
directly, which requires wrapping all such usage to transform the cost map back into a string (look at converting costs to strings for unconverted routines for an example). Other than a few edge cases that I haven't gotten around to fixing, the tests are passing.And now onto the downsides of the current implementation:
This does not cover:
Other questionable things you might see when you're looking over the changes -- I'm aware of the following (and open to suggestions):
src/cljc/i18n/en.cljc
. I'm not an expert but I'm assuming messages are converted to another format (JSON?) when going to the client and it ends up turning a some (but not all) keywords into strings.