New Core Ships - Design and Stats Discussion
Moderator: Forum Moderators
- ForestDragon
- Posts: 1857
- Joined: March 6th, 2014, 1:32 pm
- Location: Ukraine
Re: New Core Ships - Design and Stats Discussion
@Dalas Anyway, thinking further about transport, if it does get implemented, there are also some very important design questions like: What happens if a transport gets killed while having passengers?
Option A: passengers inside just die.
pros:
-slightly less likely to cause softlocks (but they are still very much possible depending on scenario design)
cons:
-for player ships, frustration of losing multiple valuable units just because one transport unit got killed (in a game where losing even one lvl2 or above unit is enough for many people to justify reloading a save)
-makes low-hp transports terrible to use
-makes the transport ability less flexible and not reusable for ground-based units where "sinking" isn't really applicable
-if transports can carry some flying/swimming units, there's not much reason for them to die from a ship sinking.
Option B: passengers are automatically dismebarked next to the ship when the ship dies
pros:
-much less frustration over losing a ship
-transport ability is more flexible for both naval and ground use
cons:
-more likely to cause softlocks if, for example, a ship carrying a leader is destroyed in the middle of the ocean and you can need your leader to reach a specified tile
-harder to implement "destroy enemy ships before they can disembark their crew" scenarios
Option A: passengers inside just die.
pros:
-slightly less likely to cause softlocks (but they are still very much possible depending on scenario design)
cons:
-for player ships, frustration of losing multiple valuable units just because one transport unit got killed (in a game where losing even one lvl2 or above unit is enough for many people to justify reloading a save)
-makes low-hp transports terrible to use
-makes the transport ability less flexible and not reusable for ground-based units where "sinking" isn't really applicable
-if transports can carry some flying/swimming units, there's not much reason for them to die from a ship sinking.
Option B: passengers are automatically dismebarked next to the ship when the ship dies
pros:
-much less frustration over losing a ship
-transport ability is more flexible for both naval and ground use
cons:
-more likely to cause softlocks if, for example, a ship carrying a leader is destroyed in the middle of the ocean and you can need your leader to reach a specified tile
-harder to implement "destroy enemy ships before they can disembark their crew" scenarios
My active add-ons: The Great Steppe Era,XP Bank,Alliances Mod,Pestilence,GSE+EoMa,Ogre Crusaders,Battle Royale,EoMaifier,Steppeifier,Hardcoreifier
My inactive add-ons (1.12): Tale of Alan, The Golden Age
Co-creator of Era of Magic
My inactive add-ons (1.12): Tale of Alan, The Golden Age
Co-creator of Era of Magic
Re: New Core Ships - Design and Stats Discussion
Btw, been viewing that you were talking about ships stats and viability.
I remember I faced this problem real long when meeting Rashy Era's Ship units. You can overview Rashy Era ships and see if anything suits to that, but as an additional note, I decided to give different resistances values to 40% and 50% water defenses ships instead of keeping both the same as Rashy Era used to be (by that I mean, I buffed res in 40% defenses one so they don't feel THAT BAD as they used to be...)
I hope you can take any ideas from rashy era ships values and see if that helps in the topic, but balancing ships to be fair and fun was not an easy task for me.
I remember I faced this problem real long when meeting Rashy Era's Ship units. You can overview Rashy Era ships and see if anything suits to that, but as an additional note, I decided to give different resistances values to 40% and 50% water defenses ships instead of keeping both the same as Rashy Era used to be (by that I mean, I buffed res in 40% defenses one so they don't feel THAT BAD as they used to be...)
I hope you can take any ideas from rashy era ships values and see if that helps in the topic, but balancing ships to be fair and fun was not an easy task for me.
Creator of: Deathmatch new in 1.12 server.
Co-creator of: Era of Magic in 1.16 server
Developer of: Empires in 1.12 server, Ageless Era in 1.10 to 1.16 servers (but innactive recently)
Try My winning Orocia Guide
Co-creator of: Era of Magic in 1.16 server
Developer of: Empires in 1.12 server, Ageless Era in 1.10 to 1.16 servers (but innactive recently)
Try My winning Orocia Guide
Re: New Core Ships - Design and Stats Discussion
To get PaBD Code ready (for the player) we just need to adjust only some lines.
In PaBD the passengers getting thrown out, up to 2 tiles, getting 8 fire damage and getting slowed.ForestDragon wrote: ↑January 1st, 2025, 9:09 pm @Dalas Anyway, thinking further about transport, if it does get implemented, there are also some very important design questions like: What happens if a transport gets killed while having passengers?
Option A: passengers inside just die.
pros:
-slightly less likely to cause softlocks (but they are still very much possible depending on scenario design)
cons:
-for player ships, frustration of losing multiple valuable units just because one transport unit got killed (in a game where losing even one lvl2 or above unit is enough for many people to justify reloading a save)
-makes low-hp transports terrible to use
-makes the transport ability less flexible and not reusable for ground-based units where "sinking" isn't really applicable
-if transports can carry some flying/swimming units, there's not much reason for them to die from a ship sinking.
Option B: passengers are automatically dismebarked next to the ship when the ship dies
pros:
-much less frustration over losing a ship
-transport ability is more flexible for both naval and ground use
cons:
-more likely to cause softlocks if, for example, a ship carrying a leader is destroyed in the middle of the ocean and you can need your leader to reach a specified tile
-harder to implement "destroy enemy ships before they can disembark their crew" scenarios
Campaigns:Vendraxis Prophecy
Porting:Across the Ocean, Forgotten Legacy, Oath of Allegiance, Palms amid Blue Dunes, Carved in Stone, The Rod of Justice
Modification: Unit Color Changer, Unit Color Variation
Porting:Across the Ocean, Forgotten Legacy, Oath of Allegiance, Palms amid Blue Dunes, Carved in Stone, The Rod of Justice
Modification: Unit Color Changer, Unit Color Variation
- ForestDragon
- Posts: 1857
- Joined: March 6th, 2014, 1:32 pm
- Location: Ukraine
Re: New Core Ships - Design and Stats Discussion
Alright.
My active add-ons: The Great Steppe Era,XP Bank,Alliances Mod,Pestilence,GSE+EoMa,Ogre Crusaders,Battle Royale,EoMaifier,Steppeifier,Hardcoreifier
My inactive add-ons (1.12): Tale of Alan, The Golden Age
Co-creator of Era of Magic
My inactive add-ons (1.12): Tale of Alan, The Golden Age
Co-creator of Era of Magic
Re: New Core Ships - Design and Stats Discussion
Can we discuss this ability?
I had different Ideas with it:
- Can't move, can't attack
- Can move, can't attack
- Can't move, can attack (your ability)
- Can move, can attack, but slowed
Campaigns:Vendraxis Prophecy
Porting:Across the Ocean, Forgotten Legacy, Oath of Allegiance, Palms amid Blue Dunes, Carved in Stone, The Rod of Justice
Modification: Unit Color Changer, Unit Color Variation
Porting:Across the Ocean, Forgotten Legacy, Oath of Allegiance, Palms amid Blue Dunes, Carved in Stone, The Rod of Justice
Modification: Unit Color Changer, Unit Color Variation
Re: New Core Ships - Design and Stats Discussion
I'd been thinking that a dying ship would unload any units it can (onto unoccupied adjacent hexes with >0% defense), who'd become slowed and lose 50% of their hp. Any units who can't be unloaded would die.ForestDragon wrote: ↑January 1st, 2025, 9:09 pm What happens if a transport gets killed while having passengers?
My tentative plans are:
- transports cannot carry any `race=ship` units, but can carry fliers, merfolk, etc
- transports can load/unload units from/to any adjacent tile. Units cannot be unloaded onto terrain in which they'd have 0% defense.
- the player can't choose to load/unload one particular unit and not others; triggering a load will load every adjacent unit (up to the cargo limit), and triggering an unload will unload every carried unit (unless there's no valid hexes). I don't think it's possible to create a good enough UI to do things differently (or maybe a right-click menu could work?)
- let the player load/unload an unlimited number of times in a turn (also let them undo)
- when unloading units, prefer higher-defense terrain, and prefer the direction the ship is facing
- show a ship's cargo as overlays; scaled down unit images.
- simulate healing/poison/resting while inside ships. Thankfully none of those can kill units.
- create custom HAVEUNIT_TRANSPORT and SPEAKER_TRANSPORT macros, to be used cases where an event unit is in a transport
- [allow_undo], and make sure [on_undo] works right
- unload all units from transports at victory (ignoring placement restrictions)
All very tentative, of course, and I'm open to changing anything.
The default AI can't handle transports at all, can it? How would it know how to trigger a load/unload, or how to use a transport to move units from one place to another?
I'd imagined that AI transports would need to be given scripted scenario-specific behavior (e.g. we spawn ships with cargo at one end of the map, give them a [goto] to reach the shore, and once they get there we script an unload).
-
- Developer
- Posts: 607
- Joined: January 6th, 2008, 3:32 am
- Location: The United Kingdom of Great America and Northern Greenland
Re: New Core Ships - Design and Stats Discussion
The default AI can handle unloading troops from ships in The High Seas since the ships use
[tunnel]
(which the default pathfinder knows how to use) to connect their "decks" (separate sections of the map that represent the deck of each ship) to all the tiles adjacent to the ship. The default AI will also board and fight to disable opposing ships in the same way. A player can do likewise and even capture an enemy ship by putting units on key parts of the deck (rudder and sail) to "man" it. Might be worth checking out.- Roge_Tebnelok
- Posts: 69
- Joined: November 19th, 2022, 3:12 pm
- Location: Янтарный Берег (Amber Coast/Bernsteinen Seeufer/Ravgul Strand-kant/Meripihka Rannan)/Elensefar
Re: New Core Ships - Design and Stats Discussion
Aren't Derelict and Ghost ships possessed or something like kept together and driven by unnatural force? Shouldn't they in that case have lower arcane resistance than other ships? Like 30-40% with 60-80% as a standard for mechanical units?
Omniscience and omnipotence are one and the same.
- ForestDragon
- Posts: 1857
- Joined: March 6th, 2014, 1:32 pm
- Location: Ukraine
Re: New Core Ships - Design and Stats Discussion
Would this be just manually simulating mainline healing/poison effects (so only regen +8 and regen +4 but not custom modded regeneration versions), or would it be a more general "trigger any [healing] or poison effects on a unit"?
Additionally, it might be a good idea to make healers in a transport heal all other units in the same transport, and having some kind of menu similar to the recall list to check current units inside a specific transport in more detail (it would also allow individual unloading, even if loading is still done for all adjacent units)
Would it be a "match if the unit is on the map OR in transport" check or only check units in transport?
My active add-ons: The Great Steppe Era,XP Bank,Alliances Mod,Pestilence,GSE+EoMa,Ogre Crusaders,Battle Royale,EoMaifier,Steppeifier,Hardcoreifier
My inactive add-ons (1.12): Tale of Alan, The Golden Age
Co-creator of Era of Magic
My inactive add-ons (1.12): Tale of Alan, The Golden Age
Co-creator of Era of Magic
Re: New Core Ships - Design and Stats Discussion
I won't weigh in too much on transport mechanics or traits/abilities, but maybe some situations can be helped or at least have more options if there are weaker support boats, maybe don't even have much of an attack, not that different from the old fake units. I got a few rough candidates, mostly based on the same hull, so the animations wouldn't be that difficult to apply to more than one of them.
The one thing I would say about the transport mechanics, and maybe this was already said and I just missed it, is that maybe it should all be kept separate from the base unit. It's a good idea to have an out-of-the-box solution for UMC, and not making it too complicated would be nice, but the issue with corner cases will always be there. Maybe the ongoing GUI work will have some effect on this anyway.Dalas120 wrote: ↑January 2nd, 2025, 4:12 pm My tentative plans are:
- transports cannot carry any `race=ship` units, but can carry fliers, merfolk, etc
- transports can load/unload units from/to any adjacent tile. Units cannot be unloaded onto terrain in which they'd have 0% defense.
- the player can't choose to load/unload one particular unit and not others; triggering a load will load every adjacent unit (up to the cargo limit), and triggering an unload will unload every carried unit (unless there's no valid hexes). I don't think it's possible to create a good enough UI to do things differently (or maybe a right-click menu could work?)
- let the player load/unload an unlimited number of times in a turn (also let them undo)
- when unloading units, prefer higher-defense terrain, and prefer the direction the ship is facing
- show a ship's cargo as overlays; scaled down unit images.
- simulate healing/poison/resting while inside ships. Thankfully none of those can kill units.
- create custom HAVEUNIT_TRANSPORT and SPEAKER_TRANSPORT macros, to be used cases where an event unit is in a transport
- [allow_undo], and make sure [on_undo] works right
- unload all units from transports at victory (ignoring placement restrictions)
All very tentative, of course, and I'm open to changing anything.
BfW 1.12 supported, but active development only for BfW 1.13/1.14: Bad Moon Rising | Trinity | Archaic Era |
| Abandoned: Tales of the Setting Sun
GitHub link for these projects
| Abandoned: Tales of the Setting Sun
GitHub link for these projects
Re: New Core Ships - Design and Stats Discussion
I dont be greedy, but I am a saurian so it can't be helped. I want them all :Odoofus-01 wrote: ↑January 5th, 2025, 1:25 am I won't weigh in too much on transport mechanics or traits/abilities, but maybe some situations can be helped or at least have more options if there are weaker support boats, maybe don't even have much of an attack, not that different from the old fake units. I got a few rough candidates, mostly based on the same hull, so the animations wouldn't be that difficult to apply to more than one of them.
skiff-examples.png
The one thing I would say about the transport mechanics, and maybe this was already said and I just missed it, is that maybe it should all be kept separate from the base unit. It's a good idea to have an out-of-the-box solution for UMC, and not making it too complicated would be nice, but the issue with corner cases will always be there. Maybe the ongoing GUI work will have some effect on this anyway.Dalas120 wrote: ↑January 2nd, 2025, 4:12 pm My tentative plans are:
- transports cannot carry any `race=ship` units, but can carry fliers, merfolk, etc
- transports can load/unload units from/to any adjacent tile. Units cannot be unloaded onto terrain in which they'd have 0% defense.
- the player can't choose to load/unload one particular unit and not others; triggering a load will load every adjacent unit (up to the cargo limit), and triggering an unload will unload every carried unit (unless there's no valid hexes). I don't think it's possible to create a good enough UI to do things differently (or maybe a right-click menu could work?)
- let the player load/unload an unlimited number of times in a turn (also let them undo)
- when unloading units, prefer higher-defense terrain, and prefer the direction the ship is facing
- show a ship's cargo as overlays; scaled down unit images.
- simulate healing/poison/resting while inside ships. Thankfully none of those can kill units.
- create custom HAVEUNIT_TRANSPORT and SPEAKER_TRANSPORT macros, to be used cases where an event unit is in a transport
- [allow_undo], and make sure [on_undo] works right
- unload all units from transports at victory (ignoring placement restrictions)
All very tentative, of course, and I'm open to changing anything.
Palms amid Blue Dunes Transportmechanic does almost all of it, execept:Dalas120 wrote: ↑January 2nd, 2025, 4:12 pmI'd been thinking that a dying ship would unload any units it can (onto unoccupied adjacent hexes with >0% defense), who'd become slowed and lose 50% of their hp. Any units who can't be unloaded would die.ForestDragon wrote: ↑January 1st, 2025, 9:09 pm What happens if a transport gets killed while having passengers?
My tentative plans are:
- transports cannot carry any `race=ship` units, but can carry fliers, merfolk, etc
- transports can load/unload units from/to any adjacent tile. Units cannot be unloaded onto terrain in which they'd have 0% defense.
- the player can't choose to load/unload one particular unit and not others; triggering a load will load every adjacent unit (up to the cargo limit), and triggering an unload will unload every carried unit (unless there's no valid hexes). I don't think it's possible to create a good enough UI to do things differently (or maybe a right-click menu could work?)
- let the player load/unload an unlimited number of times in a turn (also let them undo)
- when unloading units, prefer higher-defense terrain, and prefer the direction the ship is facing
- show a ship's cargo as overlays; scaled down unit images.
- simulate healing/poison/resting while inside ships. Thankfully none of those can kill units.
- create custom HAVEUNIT_TRANSPORT and SPEAKER_TRANSPORT macros, to be used cases where an event unit is in a transport
- [allow_undo], and make sure [on_undo] works right
- unload all units from transports at victory (ignoring placement restrictions)
All very tentative, of course, and I'm open to changing anything.
The default AI can't handle transports at all, can it? How would it know how to trigger a load/unload, or how to use a transport to move units from one place to another?
I'd imagined that AI transports would need to be given scripted scenario-specific behavior (e.g. we spawn ships with cargo at one end of the map, give them a [goto] to reach the shore, and once they get there we script an unload).
- Units cannot be unloaded onto terrain in which they'd have 0% defense. (unsure, never tested it)
- when unloading units, prefer higher-defense terrain, and prefer the direction the ship is facing
- show a ship's cargo as overlays; scaled down unit images.
- simulate healing/poison/resting while inside ships. Thankfully none of those can kill units.
- create custom HAVEUNIT_TRANSPORT and SPEAKER_TRANSPORT macros, to be used cases where an event unit is in a transport (here lets the author unload the unit and then load)
- [allow_undo], and make sure [on_undo] works right (didn't test this as well)
Campaigns:Vendraxis Prophecy
Porting:Across the Ocean, Forgotten Legacy, Oath of Allegiance, Palms amid Blue Dunes, Carved in Stone, The Rod of Justice
Modification: Unit Color Changer, Unit Color Variation
Porting:Across the Ocean, Forgotten Legacy, Oath of Allegiance, Palms amid Blue Dunes, Carved in Stone, The Rod of Justice
Modification: Unit Color Changer, Unit Color Variation
- lhybrideur
- Posts: 454
- Joined: July 9th, 2019, 1:46 pm
Re: New Core Ships - Design and Stats Discussion
For the Fireship, I would see a special status/ability called burning, where it loses some HP each turn (can kill) and is unhealable, or smth like that
- Temuchin Khan
- Posts: 1844
- Joined: September 3rd, 2004, 6:35 pm
- Location: Player 6 on the original Agaia map
Re: New Core Ships - Design and Stats Discussion
I like the new ships, overall.
I do wonder, however, if the Fireship should be a kamikaze unit.
I do wonder, however, if the Fireship should be a kamikaze unit.
Check out my new book!
https://www.amazon.com/dp/1956715029/re ... oks&sr=1-1
https://www.amazon.com/dp/1956715029/re ... oks&sr=1-1
Re: New Core Ships - Design and Stats Discussion
Ok, here's a test implementation of ABILITY_TRANSPORT. Should be feature-complete, including undo.
`transport__board_unit` and `transport__debark_unit` can be called from anywhere, giving campaign authors a way to load/unload specific units in cutscenes or for scripted AI.
I decided not to allow healing inside transports. It'd be tricky to do (especially handling non-core heal/regen abilities, like ForestDragon mentioned), and both healers and transports are very powerful even without extra synergy. Poison and slow are still tracked.
I also decided not to `set canrecruit=yes` when a transport carries a leader. While this does avoid defeat from `defeat_condition=no_leader_left`, it also means that you can stick your leader in a transport to disable upkeep for both the transport and any carried units - I don't think that's a fun incentive for the player. Instead, campaign creators can set `defeat_condition=no_units_left` or `defeat_condition=never` plus a last breath/die event for their leader.
If anyone is interested in testing, please let me know how intuitive the implementation is and if you find any bugs.
`transport__board_unit` and `transport__debark_unit` can be called from anywhere, giving campaign authors a way to load/unload specific units in cutscenes or for scripted AI.
I decided not to allow healing inside transports. It'd be tricky to do (especially handling non-core heal/regen abilities, like ForestDragon mentioned), and both healers and transports are very powerful even without extra synergy. Poison and slow are still tracked.
I also decided not to `set canrecruit=yes` when a transport carries a leader. While this does avoid defeat from `defeat_condition=no_leader_left`, it also means that you can stick your leader in a transport to disable upkeep for both the transport and any carried units - I don't think that's a fun incentive for the player. Instead, campaign creators can set `defeat_condition=no_units_left` or `defeat_condition=never` plus a last breath/die event for their leader.
If anyone is interested in testing, please let me know how intuitive the implementation is and if you find any bugs.
Code: Select all
#########################
# TRANSPORT ABILITY DEFINITION
#########################
#define ABILITY_TRANSPORT
# Canned definition of the transports ability to be included in an [abilities] clause.
# this ability is EXPERIMENTAL, and details of its behavior may change. Please report any bugs to the Wesnoth github, forum, or discord
[dummy]
id=transport
name=_"transport"
description=_"This unit can carry a number of same-side passengers equal to its level. Cramped shipboard conditions prevent passengers from resting or healing each other, although poison will continue to take effect.
To board adjacent units or to debark passengers onto an adjacent hex, <b>mouse-over the unit/hex and press “t”, or right-click</b>."
#########################
# ASSIGN HOTKEYS
#########################
# Wesnoth doesn't allow multiple menu_items with the same hotkey
# so create a single invisible menu_item here
[event]
name=unit placed
id=transport__assign_hotkeys # we already have a transport__unit_placed
[filter]
ability=transport
[/filter]
[filter_condition]
{VARIABLE_CONDITIONAL transport__hotkey_menu_item_set not_equals yes}
[/filter_condition]
{VARIABLE transport__hotkey_menu_item_set yes}
[set_menu_item]
id=transport_hotkey_menu_item
use_hotkey=only
[default_hotkey]
key=t
[/default_hotkey]
[filter_location]
[filter_adjacent_location]
[filter]
ability=transport
[/filter]
[/filter_adjacent_location]
[/filter_location]
[command]
#--------------------
# GET ALL CANDIDATE TRANSPORTS
#--------------------
[store_unit]
[filter]
side=$side_number
ability=transport
[filter_location]
[filter_adjacent_location]
x,y=$x1,$y1
[/filter_adjacent_location]
[/filter_location]
[/filter]
variable=transportHotkey_transports
[/store_unit]
[if]
[have_unit]
x,y=$x1,$y1
[/have_unit]
#--------------------
# BOARD UNIT
#--------------------
# if we don't successfully board the first transport (maybe it's full), try every other option
[then]
[foreach]
array=transportHotkey_transports
variable=transportHotkey_transport
[do]
[fire_event]
name=transport__board_unit
[primary_unit]
id=$transportHotkey_transport.id
[/primary_unit]
[secondary_unit]
x,y=$x1,$y1
[/secondary_unit]
[/fire_event]
[if]
[have_unit]
x,y=$x1,$y1
[/have_unit]
[then]
[/then]
[else]
[break]
[/break]
[/else]
[/if]
[/do]
[/foreach]
[/then]
#--------------------
# DEBARK UNIT
#--------------------
# if we don't successfully debark the transport (maybe it's empty), try every other option
[else]
[foreach]
array=transportHotkey_transports
variable=transportHotkey_transport
[do]
{VARIABLE transport__debark_hexes[0].x $x1}
{VARIABLE transport__debark_hexes[0].y $y1}
[fire_event]
name=transport__debark_unit
[primary_unit]
id=$transportHotkey_transport.id
[/primary_unit]
[/fire_event]
[if]
[have_unit]
x,y=$x1,$y1
[/have_unit]
[then]
[break]
[/break]
[/then]
[/if]
[/do]
[/foreach]
[/else]
[/if]
[allow_undo]
[/allow_undo]
{CLEAR_VARIABLE transportHotkey_transports,transportHotkey_transport,transportHotkey_passenger}
[/command]
[/set_menu_item]
[/event]
#########################
# CREATE BOARDING MENU ITEM
#########################
# not meant to be called manually
[event]
name=unit placed
id=transport__unit_placed
first_time_only=no
[filter]
ability=transport
[/filter]
# use $unit.image_icon if available, otherwise use $unit.image
[if] {VARIABLE_CONDITIONAL transport.image_icon.length greater_than 0}
[then]
{VARIABLE thumbnail $unit.image_icon}
[/then]
[else]
{VARIABLE thumbnail $unit.image}
[/else]
[/if]
#--------------------
# CREATE BOARD MENU ITEM
#--------------------
# need to wrap this in another [event], or else there's no way to set delayed_variable_substitution=no for the menu's [filter]
[event]
name=set_board_menu_item
id=transport__set_board_menu_item
delayed_variable_substitution=no
[set_menu_item]
id=board_$unit.id
description=_"Board $unit.type “$unit.name”"
image=$thumbnail|~SCALE(24,24)
[filter_location]
[filter]
side=$unit.side
[and]
side=$side_number
[/and]
[filter_adjacent]
id=$unit.id
[/filter_adjacent]
[not]
race=ship
[/not]
[not]
ability=transport
[/not]
[/filter]
[/filter_location]
[command]
[fire_event]
name=transport__board_unit
[primary_unit]
id=$unit.id
[/primary_unit]
[secondary_unit]
x,y=$|x1,$|y1
[/secondary_unit]
[/fire_event]
[allow_undo]
[/allow_undo]
[/command]
[/set_menu_item]
[/event]
[fire_event]
name=set_board_menu_item
[/fire_event]
{CLEAR_VARIABLE transport,thumbnail}
[/event]
#########################
# BOARD UNIT ONTO TRANSPORT
#########################
# [primary_unit] is the transport
# [secondary_unit] is the passenger
# (optional) manually set transport__no_animation=yes to skip the [fake_unit_move] animation
# (optional) manually set transport__silent_fail=yes to skip the [floating_text] from failed boarding
# (optional) manually set transport__skip_checks=yes to skip checks when boarding (useful for cutscenes or undo)
[event]
name=transport__board_unit
id=transport__board_unit
first_time_only=no
[filter]
side=$side_number
[/filter]
[filter_second]
side=$side_number
[not]
race=ship
[/not]
[not]
ability=transport
[/not]
[/filter_second]
#--------------------
# CHECK MOVES
#--------------------
[if]{VARIABLE_CONDITIONAL second_unit.moves less_than 1}
{VARIABLE_CONDITIONAL transport__skip_checks not_equals yes}
[then]
[if] {VARIABLE_CONDITIONAL transport__silent_fail not_equals yes}
[then]
[floating_text]
x,y=$second_unit.x,$second_unit.y
text="<span color='#ff0000' size='small'>" + _"No Moves" + "</span>"
[/floating_text]
[/then]
[/if]
[/then]
#--------------------
# CHECK CAPACITY
#--------------------
[elseif]
{VARIABLE_CONDITIONAL "passengers_$( replace_all(replace_all('$unit.id',' ',''),'-','') ).length" greater_than_equal_to $unit.level}
{VARIABLE_CONDITIONAL transport__skip_checks not_equals yes}
[then]
[if] {VARIABLE_CONDITIONAL transport__silent_fail not_equals yes}
[then]
# warn the player
[floating_text]
x,y=$unit.x,$unit.y
text="<span color='#ff0000' size='small'>" + _"Transport is Full" + "</span>"
[/floating_text]
[/then]
[/if]
[/then]
[/elseif]
[else]
#--------------------
# BOARD THE UNIT
#--------------------
[lua]
code= << wesnoth.game_events.fire('exit hex', wml.variables['second_unit'].x, wml.variables['second_unit'].y, wml.variables['unit'].x, wml.variables['unit'].y) >>
[/lua]
# can't fire 'enter hex' or 'moveto', because the transport occupies that hex and will become that event's $unit
[store_unit]
[filter]
id=$second_unit.id
[/filter]
variable="passengers_$( replace_all(replace_all('$unit.id',' ',''),'-','') )" # e.g. passengers_TransportGalleon4
mode=append
kill=yes
[/store_unit]
[if] {VARIABLE_CONDITIONAL transport__no_animation not_equals yes}
[then]
[move_unit_fake]
x=$second_unit.x,$unit.x
y=$second_unit.y,$unit.y
type=$second_unit.type
side=$second_unit.side
[/move_unit_fake]
[/then]
[/if]
[fire_event]
name=transport__refresh
[primary_unit]
id=$unit.id
[/primary_unit]
[/fire_event]
#--------------------
# CREATE DEBARK MENU ITEM
#--------------------
# use $second_unit.image_icon if available, otherwise use $second_unit.image
[if] {VARIABLE_CONDITIONAL passenger.image_icon.length greater_than 0}
[then]
{VARIABLE thumbnail $second_unit.image_icon}
[/then]
[else]
{VARIABLE thumbnail $second_unit.image}
[/else]
[/if]
# need to wrap this in another [event], or else there's no way to set delayed_variable_substitution=no for the menu's [filter]
[event]
name=set_debark_menu_item
delayed_variable_substitution=no
id=transport__set_debark_menu_item
[set_menu_item]
id="debark_$( replace_all(replace_all('$second_unit.id',' ',''),'-','') )"
description=_"Debark $second_unit.type “$second_unit.name”"
image=$thumbnail~SCALE(24,24)
[filter_location]
[not]
[filter]
[/filter]
[/not]
[filter_adjacent_location]
[filter]
side=$side_number
id=$unit.id
[/filter]
[/filter_adjacent_location]
[/filter_location]
[command]
{VARIABLE transport__debark_hexes[0].x $|x1}
{VARIABLE transport__debark_hexes[0].y $|y1}
{VARIABLE transport__passenger_id $second_unit.id}
[fire_event]
name=transport__debark_unit
[primary_unit]
id=$unit.id
[/primary_unit]
[/fire_event]
[allow_undo]
[/allow_undo]
[/command]
[/set_menu_item]
[/event]
[fire_event]
name=set_debark_menu_item
[/fire_event]
#--------------------
# UNDO BOARDING
#--------------------
# remember, delayed_variable_substitution=no inside the [on_undo],
# but delayed_variable_substitution=yes inside the [event]name=transport__debark_unit
[on_undo]
{VARIABLE transport__debark_hexes[0].x $second_unit.x}
{VARIABLE transport__debark_hexes[0].y $second_unit.y}
{VARIABLE transport__passenger_id $second_unit.id}
{VARIABLE transport__skip_checks yes}
[fire_event]
name=transport__debark_unit
[primary_unit]
id=$unit.id
[/primary_unit]
[/fire_event]
[modify_unit]
[filter]
id=$second_unit.id
[/filter]
moves= $second_unit.moves
attacks_left=$second_unit.attacks_left
resting= $second_unit.resting
[/modify_unit]
[/on_undo]
[/else]
[/if]
{CLEAR_VARIABLE transport,passenger,thumbnail}
{CLEAR_VARIABLE transport__no_animation,transport__silent_fail,transport__skip_checks}
[/event]
#########################
# DEBARK ONE UNIT FROM TRANSPORT
#########################
# [primary_unit] is the transport
# (optional) manually set transport__debark_hexes - expects at least 1 x,y pair
# (optional) manually set transport__passenger_id to unload a specific passenger
# (optional) manually set transport__no_animation=yes to skip the [fake_unit_move] animation
# (optional) manually set transport__silent_fail=yes to skip the [floating_text] from failed boarding
# (optional) manually set transport__skip_checks=yes to skip the terrain defense check (useful for undo or cutscenes)
[event]
name=transport__debark_unit
id=transport__debark_unit
first_time_only=no
# don't filter by $side_number, or else this fails when a transport dies on someone else's turn
[set_variables]
name=transportDebark__passengers # copy into $passengers so that we can more easily modify the main array during this loop
to_variable="passengers_$( replace_all(replace_all('$unit.id',' ',''),'-','') )"
[/set_variables]
[foreach]
array=transportDebark__passengers
variable=transportDebark__passenger
[do]
#--------------------
# CHOOSE PASSENGER
#--------------------
# iterate through each passenger until we find the one (if any) that we're looking for
[if]{VARIABLE_CONDITIONAL transport__passenger_id not_equals $null}
{VARIABLE_CONDITIONAL transport__passenger_id not_equals $transportDebark__passenger.id}
[then]
[continue]
[/continue]
[/then]
[/if]
#--------------------
# STORE LOCATIONS
#--------------------
# if transport__debark_hexes hasn't been provided, default to all adjacent hexes
[if]{VARIABLE_CONDITIONAL transport__debark_hexes.length equals 0}
[then]
[store_locations]
[filter_adjacent_location]
x,y=$unit.x,$unit.y
[/filter_adjacent_location]
include_borders=no
variable=transport__debark_hexes
[/store_locations]
[/then]
[/if]
[foreach]
array=transport__debark_hexes
variable=transportDebark__hex
index_var=j
[do]
#--------------------
# CHECK PASSABILITY
#--------------------
[if]
[have_unit]
x,y=$transportDebark__hex.x,$transportDebark__hex.y
[/have_unit]
# always check passability, even if transport__skip_checks=yes
[then]
[continue]
[/continue]
[/then]
[/if]
#--------------------
# DEBARK THE UNIT
#--------------------
[unstore_unit]
variable=transportDebark__passenger
x,y=$transportDebark__hex.x,$transportDebark__hex.y
[/unstore_unit]
# can't fire 'exit hex', because the transport occupies that hex and will become that event's $unit
[lua]
code= << wesnoth.game_events.fire('enter hex', wml.variables['transportDebark__hex'].x, wml.variables['transportDebark__hex'].y, wml.variables['unit'].x, wml.variables['unit'].y) >>
[/lua]
[lua]
code= << wesnoth.game_events.fire('moveto', wml.variables['transportDebark__hex'].x, wml.variables['transportDebark__hex'].y, wml.variables['unit'].x, wml.variables['unit'].y) >>
[/lua]
[store_relative_direction]
[source]
x,y=$unit.x,$unit.y
[/source]
[destination]
x,y=$transportDebark__hex.x,$transportDebark__hex.y
[/destination]
variable=transportDebark__facing
[/store_relative_direction]
[modify_unit]
[filter]
id=$transportDebark__passenger.id
[/filter]
moves=0
attacks_left=0
resting=no
fainf=$transportDebark__facing
[/modify_unit] # if you change any of the debark effects, make sure to change boarding-undo as well
[hide_unit]
x,y=$transportDebark__hex.x,$transportDebark__hex.y
[/hide_unit]
#--------------------
# CHECK TERRAIN
#--------------------
[if]
[have_unit]
id=$transportDebark__passenger.id
defense=100
[/have_unit]
{VARIABLE_CONDITIONAL transport__skip_checks not_equals yes}
[then]
[kill]
id=$transportDebark__passenger.id
[/kill]
[if] {VARIABLE_CONDITIONAL transport__silent_fail not_equals yes}
[then]
[floating_text]
x,y=$transportDebark__hex.x,$transportDebark__hex.y
text="<span color='#ff0000' size='small'>" + _"Impassable Terrain" + "</span>"
[/floating_text]
[/then]
[/if]
[continue]
[/continue]
[/then]
[/if]
#--------------------
# UPDATE UI
#--------------------
[clear_menu_item]
id="debark_$( replace_all(replace_all('$transportDebark__passenger.id',' ',''),'-','') )"
[/clear_menu_item]
{CLEAR_VARIABLE "passengers_$( replace_all(replace_all('$unit.id',' ',''),'-','') )[$i]"}
[fire_event]
name=transport__refresh
[primary_unit]
id=$unit.id
[/primary_unit]
[/fire_event]
#--------------------
# ANIMATE DEBARKING
#--------------------
[if] {VARIABLE_CONDITIONAL transport__no_animation not_equals yes}
[then]
[move_unit_fake]
x=$unit.x,$transportDebark__hex.x
y=$unit.y,$transportDebark__hex.y
type=$transportDebark__passenger.type
side=$transportDebark__passenger.side
[/move_unit_fake]
[/then]
[/if]
[unhide_unit]
x,y=$transportDebark__hex.x,$transportDebark__hex.y
[/unhide_unit]
#--------------------
# UNDO DEBARKING
#--------------------
[on_undo]
{VARIABLE transport__skip_checks yes}
[fire_event]
name=transport__board_unit
[primary_unit]
id=$unit.id
[/primary_unit]
[secondary_unit]
id=$transportDebark__passenger.id
[/secondary_unit]
[/fire_event]
[/on_undo]
[break]
[/break]
[/do]
[/foreach]
[/do]
[/foreach]
{CLEAR_VARIABLE transportDebark__passengers,transportDebark__facing}
{CLEAR_VARIABLE transport__debark_hexes,transport__passenger_id}
{CLEAR_VARIABLE transport__no_animation,transport__silent_fail,transport__skip_checks}
[/event]
#########################
# REFRESH TRANSPORT
#########################
# [primary_unit] is the transport
[event]
name=transport__refresh
id=transport__refresh
first_time_only=no
#--------------------
# RESET TRANSPORT
#--------------------
[remove_object]
id=$unit.id
object_id=passengers_overlay
[/remove_object]
# {MODIFY_UNIT id=$unit.id canrecruit no} # DISABLED (reasoning below)
[if]
[have_unit]
id=$unit.id
trait=loyal
[/have_unit]
[then]
{VARIABLE transportRefresh__upkeep 0}
[/then]
[else]
{VARIABLE transportRefresh__upkeep $unit.level}
[/else]
[/if]
#--------------------
# CHECK EACH PASSENGER
#--------------------
[foreach]
array="passengers_$( replace_all(replace_all('$unit.id',' ',''),'-','') )"
variable=transportRefresh__passenger
[do]
{CLEAR_VARIABLE transportRefresh__poisonIPF}
[if] {VARIABLE_CONDITIONAL transportRefresh__passenger.status.poisoned equals yes}
[then]
{VARIABLE transportRefresh__poisonIPF "~G(70)"}
[/then]
[/if]
[object]
id=passengers_overlay
take_only_once=no
[filter]
id=$unit.id
[/filter]
[effect]
apply_to=overlay
add=misc/blank-hex.png~SCALE(144,144)~BLIT( $transportRefresh__passenger.image|~FL()~SCALE(42,42)$transportRefresh__poisonIPF, 36,"$($i*18-8)" )
[/effect]
[/object]
{CLEAR_VARIABLE transportRefresh__poisonIPF}
# many scenarios end in defeat if the player has no leader
# so, if we load a leader, make the ship a leader
# DISABLED. 1) canrecruit=yes messes with passenger upkeep, and 2) modders can just use defeat_condition=never
# [if] {VARIABLE_CONDITIONAL transportRefresh__passenger.canrecruit equals yes}
# [then]
# {MODIFY_UNIT id=$unit.id canrecruit yes}
# [/then]
# [/if]
# NOTE - if the transport has canrecruit=yes, the engine ignores its upkeep
# to avoid passengers getting free upkeep, I recommend against making transports leaders
[if] {VARIABLE_CONDITIONAL transportRefresh__passenger.upkeep equals "full"}
[then]
{VARIABLE_OP transportRefresh__upkeep add $transportRefresh__passenger.level}
[/then]
[/if]
[/do]
[/foreach]
{MODIFY_UNIT id=$unit.id upkeep $transportRefresh__upkeep}
{CLEAR_VARIABLE transportRefresh__upkeep}
[allow_undo]
[/allow_undo]
[/event]
#########################
# TICK ALL TRANSPORTS
#########################
# should work fine if called manually
[event]
name=side turn # apparently no such thing as "side turn refresh"
id=transport__tick
first_time_only=no
[store_unit]
[filter]
side=$side_number
ability=transport
[/filter]
variable=transportTick__transports
[/store_unit]
[foreach]
array=transportTick__transports
variable=transportTick__transport
[do]
#--------------------
# HEALING, POISON, AND SLOW
#--------------------
[foreach]
array="passengers_$( replace_all(replace_all('$transportTick__transport.id',' ',''),'-','') )"
variable=transportTick__passenger
[do]
[lua]
code = <<
-- if the transport can heal or cure, record that
local transport_cures = false
local transport_slows = false
local transport_healing = 0
for ability in wml.child_range(wml.get_child(wml.variables['transportTick__transport'], 'abilities'), 'heals') do
if ability.poison=='cured' then transport_cures=true end
if ability.poison=='slowed' then transport_slows=true end
transport_healing = math.max( transport_healing, ability.value )
end
-- if the passenger can self-cure, record that
local passenger_cures = false
local passenger_slows = false
for ability in wml.child_range(wml.get_child(wml.variables['transportTick__passenger'], 'abilities'), 'regenerate') do
if ability.poison=='cured' then passenger_cures=true end
if ability.poison=='slowed' then passenger_slows=true end
end
-- implement curing and healing
-- remember that units in transports can't rest, regenerate, or heal others
if wml.get_child(wml.variables['transportTick__passenger'],'status').poisoned then
if transport_cures or passenger_cures then
wml.variables['transportTick__unpoison'] = false
elseif transport_slows or passenger_slows then
-- do nothing
else
wml.variables['transportTick__new_hitpoints'] = math.max(
1,
wml.variables['transportTick__passenger'].hitpoints-8
)
end
else
wml.variables['transportTick__new_hitpoints'] = math.min(
wml.variables['transportTick__passenger'].hitpoints+transport_healing,
wml.variables['transportTick__passenger'].max_hitpoints
)
end
>>
[/lua]
# modifying these via lua doesn't seem to work; handle via WML instead
[if] {VARIABLE_CONDITIONAL transportTick__new_hitpoints not_equals $null}
[then]
{VARIABLE transportTick__passenger.hitpoints $transportTick__new_hitpoints}
[/then]
[/if]
[if] {VARIABLE_CONDITIONAL transportTick__unpoison not_equals $null}
[then]
{VARIABLE transportTick__passenger.status.poisoned no}
[/then]
[/if]
{VARIABLE transportTick__passenger.status.slowed no}
{CLEAR_VARIABLE transportTick__new_hitpoints,transportTick__unpoison}
[/do]
[/foreach]
#--------------------
# REFERSH TRANSPORT
#--------------------
[fire_event]
name=transport__refresh
[primary_unit]
id=$transportTick__transport.id
[/primary_unit]
[/fire_event]
[/do]
[/foreach]
{CLEAR_VARIABLE transportTick__transports}
[/event]
#########################
# TRANSPORT DIES
#########################
# not meant to be called manually
# when a transport dies, attempt to debark all passengers
# any passengers who aren't able to debark die
[event]
name=last breath
id=transport__last_breath
first_time_only=no
[filter]
ability=transport
[/filter]
#--------------------
# ATTEMPT TO DEBARK PASSENGERS
#--------------------
[set_variables]
name=transportDie__passengers
to_variable="passengers_$( replace_all(replace_all('$unit.id',' ',''),'-','') )"
[/set_variables]
[foreach]
array=transportDie__passengers
variable=transportDie__passenger
[do]
# attempt to debark this passenger
{VARIABLE transport__passenger_id $transportDie__passenger.id}
{VARIABLE transport__silent_fail yes}
[fire_event]
name=transport__debark_unit
[primary_unit]
id=$unit.id
[/primary_unit]
[/fire_event]
# if the passenger debarked, damage them for half of their current hp
{STORE_UNIT_VAR id=$transportDie__passenger.id hitpoints transportDie__damage}
{VARIABLE_OP transportDie__damage divide 2}
[harm_unit]
[filter]
id=$transportDie__passenger.id
[/filter]
amount=$transportDie__damage
kill=no
animate=yes
delay=0
[/harm_unit]
{CLEAR_VARIABLE transportDie__damage}
[/do]
[/foreach]
{CLEAR_VARIABLE transportDie__passengers}
#--------------------
# KILL ANY REMAINING PASSENGERS
#--------------------
# let the ship properly die, so that we award XP to whoever killed it and fire any death events
[event]
name=die
id=transport__die
[filter]
id=$unit.id
[/filter]
[kill]
id=$unit.id # remove the dead ship from the map, so we can kill remaining passengers in its hex
[/kill]
[set_variables]
name=transportDie__passengers
to_variable="passengers_$( replace_all(replace_all('$unit.id',' ',''),'-','') )"
[/set_variables]
[foreach]
array=transportDie__passengers
variable=transportDie__passenger
[do]
[unstore_unit]
variable=transportDie__passenger
x,y=$unit.x,$unit.y
[/unstore_unit]
[kill]
id=$transportDie__passenger.id
animate=yes
fire_event=yes
[/kill]
{CLEAR_VARIABLE "passengers_$( replace_all(replace_all('$unit.id',' ',''),'-','') )[$i]"}
[clear_menu_item]
id="debark_$( replace_all(replace_all('$transportDie__passenger.id',' ',''),'-','') )"
[/clear_menu_item]
[/do]
[/foreach]
{CLEAR_VARIABLE transportDie__passengers}
[/event]
[/event]
#########################
# DISEMBARK ON VICTORY
#########################
# don't let units stay on transports after the scenario ends, or we'd get all kinds of weird problems
# this event can also be fired manually anytime we want to disembark all embarked units (e.g. before a cutscene)
[event]
name=victory,transport__victory
id=transport__victory
first_time_only=no
[store_unit]
[filter]
ability=transport
[/filter]
variable=transportVictory__transports
[/store_unit]
[foreach]
array=transportVictory__transports
variable=transportVictory__transport
[do]
#--------------------
# DEBARK PASSENGERS
#--------------------
[set_variables]
name=transportVictory__passengers
to_variable="passengers_$( replace_all(replace_all('$transportVictory__transport.id',' ',''),'-','') )"
[/set_variables]
[foreach]
array=transportVictory__passengers
variable=transportVictory__passenger
[do]
# attempt to debark the unit
{VARIABLE transport__passenger_id $transportVictory__passenger.id}
{VARIABLE transport__silent_fail yes}
{VARIABLE transport__no_animation yes}
[fire_event]
name=transport__debark_unit
[primary_unit]
id=$transportVictory__transport.id
[/primary_unit]
[/fire_event]
# it's possible there's no adjacent free hexes in which to debark
# in that case, pick any empty hex and debark there
# this will also fail if every single hex on the map is filled, but that's very unlikely
[if]
[not]
[have_unit]
id=$transportVictory__passenger.id
[/have_unit]
[/not]
[then]
[store_locations]
[not]
[filter]
[/filter]
[/not]
include_borders=no
variable=transport__debark_hexes
[/store_locations]
{VARIABLE transport__passenger_id $transportVictory__passenger.id}
{VARIABLE transport__no_animation yes}
{VARIABLE transport__silent_fail yes}
{VARIABLE transport__skip_checks yes}
[fire_event]
name=transport__debark_unit
[primary_unit]
id=$transportVictory__transport.id
[/primary_unit]
[/fire_event]
[/then]
[/if]
[/do]
[/foreach]
#--------------------
# REFERSH TRANSPORT
#--------------------
[fire_event]
name=transport__refresh
[primary_unit]
id=transportVictory__transports
[/primary_unit]
[/fire_event]
[/do]
[/foreach]
{CLEAR_VARIABLE transportVictory__transports,transportVictory__passengers}
[/event]
[/dummy]
#enddef
Last edited by Dalas120 on January 8th, 2025, 10:56 pm, edited 8 times in total.
Re: New Core Ships - Design and Stats Discussion
You forgot event id.