Dismissal Mod

Discussion of all aspects of multiplayer development: unit balancing, map development, server development, and so forth.

Moderator: Forum Moderators

Post Reply
User avatar
Efinari
Posts: 103
Joined: January 25th, 2011, 3:07 am
Location: Grakvr Peak, Eastlands

Dismissal Mod

Post by Efinari »

(Apologies if this is the wrong board for a multiplayer mod)

This thread is for discussion of my new multiplayer mod, the Dismissal Mod.

Basically this mod allows you to dismiss units during a mp scenario (or campaign). At any point during your turn, right-click (context-click) one of your units and select the "Dismiss" option. Be warned, however: dismissing a critical unit (one whose death results in defeat) will cause you to lose the scenario, even if "Dismiss to Recall List" is turned on. Leaders (units with "canrecruit=yes") cannot be dismissed at all.

There are several configuration options available on this mod:

Require Full Health:
It is highly recommended that you leave this checked. This restricts you to dismissing only units that are at maximum health. This prevents you from whisking a cherished unit onto the recall list (see below) moments before death. It also keeps you from destroying a high-level unit just before your opponent can defeat it and gain the experience.

Dismiss to Recall List:
If your intent is to spare the upkeep now but use the unit later, you can use this option. Dismissed units will appear in the recall list instead of disappearing entirely. You can then pay the 20 gold at a later point in time to get the unit back.

Dismissal Costs:
If you want a bit more strategy in the mod, check this option. Units will expect severance pay (similar to a recruitment fee) based on their level. The slider below modifies how much gold per level the unit demands (multiplied by the unit's level or 0.5 for level 0 units). If you don't have the gold to pay the dismissal fee, you can't dismiss the unit.
Kallin Efinari
---Creator of "LOTE Unit Release", "7p - Afterwards", and "Chain Lightning"
(Working on 1.12 port of "7p - Afterwards")
Managing compatibility between 4000K people's ideas of what happens? Forget it!
gfgtdf
Developer
Posts: 1432
Joined: February 10th, 2013, 2:25 pm

Re: Dismissal Mod

Post by gfgtdf »

1) There is a known OOS bug in wesnoth 1.12 that happen when you recall a unit in the same scneario where it was initially created, so in wesnoth 1.12 you might want to add an option to prevent users form recalling in a scenario that was 'stored' in the same scenario (assuming 'Dismiss to Recall List').

2) maybe it make sense to have an option to only allow to dismiss unit on location that can also be used for recruiting units, that is on a castle when you have a leader on a connected keep.
Scenario with Robots SP scenario (1.11/1.12), allows you to build your units with components, PYR No preperation turn 1.12 mp-mod that allows you to select your units immideately after the game begins.
User avatar
Efinari
Posts: 103
Joined: January 25th, 2011, 3:07 am
Location: Grakvr Peak, Eastlands

Re: Dismissal Mod

Post by Efinari »

gfgtdf wrote:There is a known OOS bug in wesnoth 1.12 that happen when you recall a unit in the same scneario where it was initially created, so in wesnoth 1.12 you might want to add an option to prevent users form recalling in a scenario that was 'stored' in the same scenario (assuming 'Dismiss to Recall List').
I was not aware of that, thanks for the heads up. I'll have to look into that and see what I can do to work around it.
gfgtdf wrote:maybe it make sense to have an option to only allow to dismiss unit on location that can also be used for recruiting units, that is on a castle when you have a leader on a connected keep.
I thought about this, but I don't even know how to begin coding it. Besides, it does make logical sense to recruit at your keep but dismiss the units wherever they are.
On a side note, this is one of the reasons I added the "Require Full Health" option. If a unit is far away from your keep in enemy territory and under enemy fire, you can't just whisk him out of there.
Kallin Efinari
---Creator of "LOTE Unit Release", "7p - Afterwards", and "Chain Lightning"
(Working on 1.12 port of "7p - Afterwards")
Managing compatibility between 4000K people's ideas of what happens? Forget it!
Can-ned_Food
Posts: 217
Joined: December 17th, 2015, 10:27 pm

Re: Dismissal Mod

Post by Can-ned_Food »

I worked it a little.
Summary of changes:
  • option to only permit dismissal on castles
  • option to control the maximum level allowed to be dismissed
  • made it so the dismissal to recall list is only available for 1.13 or later
I did not test this.

Code: Select all

diff -r Dismissal_Mod/dismissal_mod.cfg "Dismissal_Mod new/dismissal_mod.cfg"
1,5c1,9
< #define DISMISSAL_MOD_DISMISS_UNIT SIDE X Y GOLD
<     [store_side]
<       side={SIDE}
<       variable=dismissal_stored_side
<     [/store_side]
---
> #define       Dismissal_Mod_prolif-menu-items coef
>     [set_variable]
>         name=lvl
>         #ifver        WESNOTH_VERSION >= 1.13.5
>             value=$( trunc({coef}) )
>         #else
>             value=$( floor({coef}) )
>         #endif
>     [/set_variable]
7,8c11,12
<       name=dismissal_mod_gold_cost
<       value={GOLD}
---
>         name=dismissal_gold_cost
>         value=$( round($Dismissal_Costs_Value * {coef}) )
11,131c15,207
<       [variable]
<           name=dismissal_stored_side.gold
<           less_than=$dismissal_mod_gold_cost
<       [/variable]
<       [and]
<           [variable]
<               name=dismissal_mod_gold_cost
<               greater_than=0
<           [/variable]
<       [/and]
<       [then]
<           # This would be great if it were a centered message,
<           # like the matching 'recruit failed' message.
<           [message]
<               speaker=narrator
<               message=_"You don't have enough gold to dismiss that unit"
<           [/message]
<       [/then]
<       [else]
<           # Here is where a nice person would insert an "Are
<           # you sure?" message for good units - loyal units,
<           # heroes, high-level units, etc.
<           [gold]
<               side=$side_number
<               amount=$($dismissal_mod_gold_cost * -1)
<           [/gold]
<           [store_unit]
<               [filter]
<                   x,y={X},{Y}
<               [/filter]
<               variable=stored_dismissal
<               kill=no
<           [/store_unit]
<           [if]
<               [variable]
<                   name=Dismissal_Requires_Full_Health
<                   equals="yes"
<               [/variable]
<               [then]
<                   [if]
<                       [variable]
<                           name=stored_dismissal.hitpoints
<                           equals=$stored_dismissal.max_hitpoints
<                       [/variable]
<                       [then]
<                           [if]
<                               [variable]
<                                   name=Dismissal_to_Recall_List
<                                   value="yes"
<                               [/variable]
<                               [then]
<                                   [kill]
<                                       x,y={X},{Y}
<                                       animate=no
<                                       fire_event=yes
<                                   [/kill]
<                                   [set_variable]
<                                       name=stored_dismissal.x
<                                       value=recall_x
<                                   [/set_variable]
<                                   [set_variable]
<                                       name=stored_dismissal.y
<                                       value=recall_y
<                                   [/set_variable]
<                                   [unstore_unit]
<                                       variable=stored_dismissal
<                                   [/unstore_unit]
<                               [/then]
<                               [else]
<                                   [kill]
<                                       x,y={X},{Y}
<                                       animate=no
<                                       fire_event=yes
<                                   [/kill]
<                               [/else]
<                           [/if]
<                       [/then]
<                       [else]
<                           [message]
<                               x,y={X},{Y}
<                               message=_"Let me rest before dismissing me!"
<                           [/message]
<                       [/else]
<                   [/if]
<               [/then]
<               [else]
<                   [if]
<                       [variable]
<                           name=Dismissal_to_Recall_List
<                           value="yes"
<                       [/variable]
<                       [then]
<                           [kill]
<                               x,y={X},{Y}
<                               animate=no
<                               fire_event=yes
<                           [/kill]
<                           [set_variable]
<                               name=stored_dismissal.x
<                               value=recall_x
<                           [/set_variable]
<                           [set_variable]
<                               name=stored_dismissal.y
<                               value=recall_y
<                           [/set_variable]
<                           [unstore_unit]
<                               variable=stored_dismissal
<                           [/unstore_unit]
<                       [/then]
<                       [else]
<                           [kill]
<                               x,y={X},{Y}
<                               animate=no
<                               fire_event=yes
<                           [/kill]
<                       [/else]
<                   [/if]
<               [/else]
<           [/if]
<           {CLEAR_VARIABLE stored_dismissal}
<       [/else]
---
>         [variable]
>             name=dismissal_gold_cost
>             numerical_equals=0
>         [/variable]
>         [then]
>             [set_variable]
>                 name=tag
>                 literal=" "
>             [/set_variable]
>         [/then]
>         [else]
>             [set_variable]
>                 name=tag
>                 value="  – $dismissal_gold_cost gp"
>             [/set_variable]
>         [/else]
>     [/if]
>     [if]
>         [variable]
>             name=Dismissal_Requires_Castle
>             equals=yes
>         [/variable]
>         [then]
>             [set_variable]
>                 name=terrain
>                 literal="C*,*^C*"
>             [/set_variable]
>         [/then]
>         [else]
>             [set_variable]
>                 name=terrain
>                 literal="*"
>             [/set_variable]
>         [/else]
>         ### TODO
>         ### - when StandardLocationFilter allows examining for recruit_onto= then change this
>     [/if]
>     [set_menu_item]
>         id=Dismissal_Option_$lvl
>         description=_"Dismiss Troop" + $tag
>         image="themes/upkeep.png"
>         [show_if]
>             [have_unit]
>                 x,y=$x1,$y1
>                 side=$side_number
>                 canrecruit=no
>                 level=$lvl
>             [/have_unit]
>         [/show_if]
>         [filter_location]
>             terrain=$terrain
>             ### TODO
>             ### -     see above
>         [/filter_location]
>         [command]
>             {Dismissal_Mod_dismiss-troop $side_number $x1 $y1 }
>             {CLEAR_VARIABLE dismissal_gold_cost }
>             {CLEAR_VARIABLE lvl }
>         [/command]
>     [/set_menu_item]
> #enddef
> 
> #define       Dismissal_Mod_dismiss-troop SIDE X Y
>     [store_side]
>         side={SIDE}
>         variable=dismissal_stored_side
>     [/store_side]
>     [store_unit]
>         [filter]
>             x,y={X},{Y}
>         [/filter]
>         variable=stored_dismissal
>         kill=no
>     [/store_unit]
>     [if]
>         [variable]
>             name=dismissal_stored_side.gold
>             less_than=$dismissal_gold_cost
>         [/variable]
>         [and]
>             [variable]
>                 name=dismissal_gold_cost
>                 greater_than=0
>             [/variable]
>         [/and]
>         [then]
>             ##  This would be great if it were a centered message,
>             ##  like the matching 'recruit failed' message.
>             ### TODO
>             ### -     Lua
>             [message]
>                 speaker=narrator
>                 message=_"You don't have enough gold to dismiss that troop."
>             [/message]
>         [/then]
>         [else]
>             ##  Here is where a nice person would insert an "Are
>             ##  you sure?" message for good units - loyal units,
>             ##  heroes, high-level units, etc.
>             [gold]
>                 side=$side_number
>                 amount=$( $dismissal_gold_cost * -1 )
>             [/gold]
>             [if]
>                 [variable]
>                     name=Dismissal_Requires_Full_Health
>                     equals="yes"
>                 [/variable]
>                 [then]
>                     [if]
>                         [variable]
>                             name=stored_dismissal.hitpoints
>                             equals=$stored_dismissal.max_hitpoints
>                         [/variable]
>                         [then]
>                             [if]
>                                 [variable]
>                                     name=Dismissal_to_Recall_List
>                                     value="yes"
>                                 [/variable]
>                                 [then]
>                                     [kill]
>                                         x,y={X},{Y}
>                                         animate=no
>                                         fire_event=yes
>                                     [/kill]
>                                     [set_variable]
>                                         name=stored_dismissal.x
>                                         value=recall_x
>                                     [/set_variable]
>                                     [set_variable]
>                                         name=stored_dismissal.y
>                                         value=recall_y
>                                     [/set_variable]
>                                     [unstore_unit]
>                                         variable=stored_dismissal
>                                     [/unstore_unit]
>                                 [/then]
>                                 [else]
>                                     [kill]
>                                         x,y={X},{Y}
>                                         animate=no
>                                         fire_event=yes
>                                     [/kill]
>                                 [/else]
>                             [/if]
>                         [/then]
>                         [else]
>                           ### TODO
>                           ### -       Lua
>                             [message]
>                                 x,y={X},{Y}
>                                 message=_"Allow me to heal prior to my dismissal!"
>                             [/message]
>                         [/else]
>                     [/if]
>                 [/then]
>                 [else]
>                     [if]
>                         [variable]
>                             name=Dismissal_to_Recall_List
>                             value="yes"
>                         [/variable]
>                         [then]
>                             [kill]
>                                 x,y={X},{Y}
>                                 animate=no
>                                 fire_event=yes
>                             [/kill]
>                             [set_variable]
>                                 name=stored_dismissal.x
>                                 value=recall_x
>                             [/set_variable]
>                             [set_variable]
>                                 name=stored_dismissal.y
>                                 value=recall_y
>                             [/set_variable]
>                             [unstore_unit]
>                                 variable=stored_dismissal
>                             [/unstore_unit]
>                         [/then]
>                         [else]
>                             [kill]
>                                 x,y={X},{Y}
>                                 animate=no
>                                 fire_event=yes
>                             [/kill]
>                         [/else]
>                     [/if]
>                 [/else]
>             [/if]
>             {CLEAR_VARIABLE stored_dismissal }
>         [/else]
133,134c209,239
<     {CLEAR_VARIABLE dismissal_stored_side}
<     {CLEAR_VARIABLE dismissal_gold_cost}
---
>     {CLEAR_VARIABLE dismissal_stored_side }
>     {CLEAR_VARIABLE dismissal_gold_cost }
> #enddef
> 
> #define       Dismissal_Mod_inclusion
>     [event]
>         name=start
>         {Dismissal_Mod_prolif-menu-items 0.5 }
>         [set_variable]
>             name=iter
>             value=1
>         [/set_variable]
>         [while]
>             ##  iterate through the maximum range of levels for which dismissal is available
>             ##  — absurd, but this program language is limited
>             ##  this is done to allow the menu item only for each integer along the range to $Dismissal_Max_Level — a better way to do it would be, of course, to allow something like
>             ##        [have_unit]
>             ##        level < $Dismissal_Max_Level
>             [variable]
>                 name=iter
>                 less_than=$( $Dismissal_Max_Level + 1 )
>             [/variable]
>             [do]
>                 {Dismissal_Mod_prolif-menu-items $iter }
>                 [set_variable]
>                     name=iter
>                     add=1
>                 [/set_variable]
>             [/do]
>         [/while]
>     [/event]
140,141c245,246
<     description=_"This mod adds a context (right-click) menu option to a unit where you can dismiss the unit from service.
< ---Efinari"
---
>     description=_"This mod adds a context (right-click) menu option to a troop where you can dismiss the troop from service.
>   ---Efinari"
144,170c249,290
<       [checkbox]
<           id=Dismissal_Requires_Full_Health
<           default=yes
<           name=_"Require Full Health"
<           description=_"You can only dismiss a unit with full health"
<       [/checkbox]
<       [checkbox]
<           id=Dismissal_to_Recall_List
<           default=no
<           name=_"Dismiss to Recall List"
<           description=_"Instead of deleting the unit, place it back to your side's recall list"
<       [/checkbox]
<       [checkbox]
<           id=Dismissal_Costs_On
<           default=no
<           name=_"Dismissal costs"
<           description=_"Dismissal costs gold based on the unit's level"
<       [/checkbox]
<       [slider]
<           id=Dismissal_Costs_Value
<           default=2
<           min=2
<           max=20
<           step=2
<           name=_"Cost per level"
<           description=_"Multiplied by the unit's level (0.5 for lvl 0) as the dismissal cost"
<       [/slider]
---
>         [checkbox]
>             id=Dismissal_Requires_Full_Health
>             default=yes
>             name=_"Require Full Health"
>             description=_"You can only dismiss a troop with full health"
>         [/checkbox]
>         #ifver        WESNOTH_VERSION >= 1.13
>             ### HACK
>             ###       a severe curtailing of functionality to avoid OOS when recalling from list
>             [checkbox]
>                 id=Dismissal_to_Recall_List
>                 default=no
>                 name=_"Dismiss to Recall List"
>                 description=_"Instead of permanently deleting the troop, place it in to your side's recall list"
>             [/checkbox]
>         #endif
>         [checkbox]
>             id=Dismissal_Requires_Castle
>             default=no
>             name=_"Dismissal Only on Castle"
>             description=_"Dismissal is only possible on Castle terrain"
>             ### TODO
>             ### -     when the criteria are changed, revise this
>         [/checkbox]
>         [slider]
>             id=Dismissal_Max_Level
>             default=5
>             min=0
>             max=11
>             step=1
>             name=_"Maximum Level"
>             description=_"The maximum tier of troop level for which dismissal is possible; troops at levels above this cannot be dismissed"
>         [/slider]
>         [slider]
>             id=Dismissal_Costs_Value
>             default=2
>             min=0
>             max=20
>             step=2
>             name=_"Cost per level"
>             description=_"Multiplied by the troop's level (0.5 for lvl 0) as the dismissal cost; a value of 0 makes it cost no gold to dismiss troops"
>         [/slider]
172,321c292
<     
<     [event]
<       name=start
<       [if]
<           [variable]
<               name=Dismissal_Costs_On
<               equals="yes"
<           [/variable]
<           [then]
<               [set_menu_item]
<                   id=Dismissal_Option_lvl0
<                   description=_"Dismiss Unit - $(round($Dismissal_Costs_Value * 0.5)) gp"
<                   image="themes/upkeep.png"
<                   [show_if]
<                       [have_unit]
<                           x,y=$x1,$y1
<                           side=$side_number
<                           canrecruit=no
<                           level=0
<                       [/have_unit]
<                   [/show_if]
<                   [command]
<                       [set_variable]
<                           name=dismissal_gold_cost
<                           value=$(round($Dismissal_Costs_Value * 0.5))
<                       [/set_variable]
<                       {DISMISSAL_MOD_DISMISS_UNIT $side_number $x1 $y1 $dismissal_gold_cost}
<                       {CLEAR_VARIABLE dismissal_gold_cost}
<                   [/command]
<               [/set_menu_item]
<               [set_menu_item]
<                   id=Dismissal_Option_lvl1
<                   description=_"Dismiss Unit - $Dismissal_Costs_Value gp"
<                   image="themes/upkeep.png"
<                   [show_if]
<                       [have_unit]
<                           x,y=$x1,$y1
<                           side=$side_number
<                           canrecruit=no
<                           level=1
<                       [/have_unit]
<                   [/show_if]
<                   [command]
<                       {DISMISSAL_MOD_DISMISS_UNIT $side_number $x1 $y1 $Dismissal_Costs_Value}
<                   [/command]
<               [/set_menu_item]
<               [set_menu_item]
<                   id=Dismissal_Option_lvl2
<                   description=_"Dismiss Unit - $($Dismissal_Costs_Value * 2) gp"
<                   image="themes/upkeep.png"
<                   [show_if]
<                       [have_unit]
<                           x,y=$x1,$y1
<                           side=$side_number
<                           canrecruit=no
<                           level=2
<                       [/have_unit]
<                   [/show_if]
<                   [command]
<                       [set_variable]
<                           name=dismissal_gold_cost
<                           value=$($Dismissal_Costs_Value * 2)
<                       [/set_variable]
<                       {DISMISSAL_MOD_DISMISS_UNIT $side_number $x1 $y1 $dismissal_gold_cost}
<                       {CLEAR_VARIABLE dismissal_gold_cost}
<                   [/command]
<               [/set_menu_item]
<               [set_menu_item]
<                   id=Dismissal_Option_lvl3
<                   description=_"Dismiss Unit - $($Dismissal_Costs_Value * 3) gp"
<                   image="themes/upkeep.png"
<                   [show_if]
<                       [have_unit]
<                           x,y=$x1,$y1
<                           side=$side_number
<                           canrecruit=no
<                           level=3
<                       [/have_unit]
<                   [/show_if]
<                   [command]
<                       [set_variable]
<                           name=dismissal_gold_cost
<                           value=$($Dismissal_Costs_Value * 3)
<                       [/set_variable]
<                       {DISMISSAL_MOD_DISMISS_UNIT $side_number $x1 $y1 $dismissal_gold_cost}
<                       {CLEAR_VARIABLE dismissal_gold_cost}
<                   [/command]
<               [/set_menu_item]
<               [set_menu_item]
<                   id=Dismissal_Option_lvl4
<                   description=_"Dismiss Unit - $($Dismissal_Costs_Value * 4) gp"
<                   image="themes/upkeep.png"
<                   [show_if]
<                       [have_unit]
<                           x,y=$x1,$y1
<                           side=$side_number
<                           canrecruit=no
<                           level=4
<                       [/have_unit]
<                   [/show_if]
<                   [command]
<                       [set_variable]
<                           name=dismissal_gold_cost
<                           value=$($Dismissal_Costs_Value * 4)
<                       [/set_variable]
<                       {DISMISSAL_MOD_DISMISS_UNIT $side_number $x1 $y1 $dismissal_gold_cost}
<                       {CLEAR_VARIABLE dismissal_gold_cost}
<                   [/command]
<               [/set_menu_item]
<               [set_menu_item]
<                   id=Dismissal_Option_lvl5
<                   description=_"Dismiss Unit - $($Dismissal_Costs_Value * 5) gp"
<                   image="themes/upkeep.png"
<                   [show_if]
<                       [have_unit]
<                           x,y=$x1,$y1
<                           side=$side_number
<                           canrecruit=no
<                           level=5
<                       [/have_unit]
<                   [/show_if]
<                   [command]
<                       [set_variable]
<                           name=dismissal_gold_cost
<                           value=$($Dismissal_Costs_Value * 5)
<                       [/set_variable]
<                       {DISMISSAL_MOD_DISMISS_UNIT $side_number $x1 $y1 $dismissal_gold_cost}
<                       {CLEAR_VARIABLE dismissal_gold_cost}
<                   [/command]
<               [/set_menu_item]
<           [/then]
<           [else]
<               [set_menu_item]
<                   id=Dismissal_Option
<                   description=_"Dismiss Unit"
<                   image="themes/units.png"
<                   [show_if]
<                       [have_unit]
<                           x,y=$x1,$y1
<                           side=$side_number
<                           canrecruit=no
<                       [/have_unit]
<                   [/show_if]
<                   [command]
<                       {DISMISSAL_MOD_DISMISS_UNIT $side_number $x1 $y1 0}
<                   [/command]
<               [/set_menu_item]
<           [/else]
<       [/if]
<     [/event]    
---
>     {Dismissal_mod_inclusion }
User avatar
Sapient
Inactive Developer
Posts: 4453
Joined: November 26th, 2005, 7:41 am
Contact:

Re: Dismissal Mod

Post by Sapient »

Be careful with using FormulaAI Language inside unquoted attributes:

Code: Select all

less_than=$( $Dismissal_Max_Level + 1 )
The plus sign will be ripped out of the formula (because it is unquoted, and therefore signifies concatenation).

P.S. You can use formula="level < $Dismissal_Max_Level" inside standard unit filters, e.g. [have_unit].
http://www.wesnoth.org/wiki/User:Sapient... "Looks like your skills saved us again. Uh, well at least, they saved Soarin's apple pie."
User avatar
Efinari
Posts: 103
Joined: January 25th, 2011, 3:07 am
Location: Grakvr Peak, Eastlands

Re: Dismissal Mod

Post by Efinari »

Can-ned_Food wrote:I worked it a little.
Summary of changes:
  • option to only permit dismissal on castles
  • option to control the maximum level allowed to be dismissed
  • made it so the dismissal to recall list is only available for 1.13 or later
Hey, these are some neat changes. I'll have to find some time to look through the code and implement these at some point. Couple things here:
Castle dismissal - I guess it does make sense to dismiss at any old castle. I just wish it could detect if your leader was on a keep connected to that castle.
Max level - I like this even more because it makes the mod scalable, instead of the original fixed values.
Sapient wrote:Be careful with using FormulaAI Language inside unquoted attributes:
CODE: SELECT ALL
less_than=$( $Dismissal_Max_Level + 1 )
The plus sign will be ripped out of the formula (because it is unquoted, and therefore signifies concatenation).
P.S. You can use formula="level < $Dismissal_Max_Level" inside standard unit filters, e.g. [have_unit].
Thanks for the tip. I'll be sure to look into that as well.

Thanks for the help here. :)

P.S. Sorry it took a couple months to respond. I haven't been monitoring my posts lately like I should.
Kallin Efinari
---Creator of "LOTE Unit Release", "7p - Afterwards", and "Chain Lightning"
(Working on 1.12 port of "7p - Afterwards")
Managing compatibility between 4000K people's ideas of what happens? Forget it!
Post Reply