Quantcast
Channel: forum.freeciv.org
Viewing all articles
Browse latest Browse all 354

Rulesets and modpacks • TUTORIAL: Unit with charging system and rechargeable

$
0
0
Level needed -
* Ruleset: Basics
* Scipt LUA: none, 0, nadica

Hey guys! Firstly, I want to thank all the developers, especially cazfi and Ignatus who helped me create the units I wanted and also make this tutorial possible.

Based on their idea, it is possible to make several units with different actions.
Their idea is simple, use extras (extra terrain) as counters and flags.
This way it is possible to count how many charges each unit spent.
To do this, the unit must be Unique ("Unique" flag).
This is because it is not possible to associate each unit with an extra.
So if the unit has the "Unique" flag, or if it is possible to create a unit per city, it is then possible to store the counter (unit charges) on the land of its hometown.
So, with a little scripting in Lua, i could make the following units:

Actions in cities with varied effects:
Marshall, Clown, TaxMen, Cleaners, General and Constructor.

Actions on units (using "Heal Unit" and "Heal Unit 2")
Healer, Doctor, Carpenter and Mechanic

Actions on tiles
Archaeologist (just like civ6) (my favorite one)

It is possible to control the effects in cities for several turns.
Different effects in each turn or in a range of turns.
Unfortunately I had to stop because I discovered the limit of 45 flags. So sad :(

So I want to share my script in LUA and how to use with your own units.

How it works:
1) The unit perform the action to a city, unit or a tile
2) If action is successful, the script places an extra in his hometown (pay one charge);
3) If the charges run out, the unit disbands;
4) If the unit is in the city with a specific building for a turn without moving, recharge completely.

Our example (Doctor) will have 5 charges, and can recharge them in a city with Temples.

So, briefly we need:

a) The unit
- Create 1 flag. Example: "Healer";
- Create 1 unit. Example: "The Doctors".

b) The charges
- Create 4 extras to control the charges (5 charges)

c) The effects
- Configure the effects.

d) The game
- Configure unit actions.

e) The script
- Copy, paste and cross your fingers.


To take a ruleset as a example, copy the entire classic folder and the classic.serv file
(usually located in /INSTALLED_FOLDER/share/freeciv/ ) to your own rules folder (usually /.freeciv/3.1)
I used 3.1 version, and I DONT believe that other previous versions (like 3.0) should work.
This is due to the fact that the "action_finished_unit_unit" function is from version 3.1

Lets begin!

a) The unit

Open the file units.ruleset, put the flag "Healer" in the "flags" property, right below "Bomber".

File: units.ruleset

Code:

flags =  { "name", "helptxt"... ... _("Bomber"), _("Bad at attacking Fighters")_("Healer")  }
And to make testing easier, copy the "Explorer" unit and paste it at the end of the file.
Change the "tech_req" property and the "obsolete_by" property to "None".
Add the "Healer" and "Unique" flags in the end of the "flags" property.
Comment the line that contains the "roles" property (add a semicolon at the beginning of the line).

File: units.ruleset

Code:

; ----------------------------------------;  UNIT The Doctors (copied from Explorer); ----------------------------------------[unit_doctors]name          = _("The Doctors")tech_req      = "None"obsolete_by   = "None"......flags         = "IgTer", "IgZOC", "NonMil", "HasNoZOC", "Healer", "Unique"; roles         = "Explorer", "ExplorerStartUnit"  ......; ----------------------------------------
b) The charges

Now let's create the charges for the unit.
In our example, we need 4 new extras to control its charges.
The rule is simple, 5 charges = 4 new extras. If you want 10 charges = 9 new extras

ATTENTION:
The "rule_name" property must start with the same name as the flag.
And end with "_CHARGE_1" or "_CHARGE_2" or "_CHARGE_3", etc...
Open the terrain.ruleset file and place at the end of it:

File: terrain.ruleset

Code:

; ----------------------------------------; DOCTOR - CHARGES ; (5 charges => "_CHARGE_1" until "_CHARGE_4" ); ----------------------------------------[extra_healer_charges_1]name         = _("Doctor used charge 1")rule_name    = "Healer_CHARGE_1"category     = "Bonus"graphic      = "extra.ruins"activity_gfx = "None"rmact_gfx    = "None"buildable    = FALSEhelptext     = ""; ----------------------------------------[extra_healer_charges_2]name         = _("Doctor used charge 2")rule_name    = "Healer_CHARGE_2"category     = "Bonus"graphic      = "extra.ruins"activity_gfx = "None"rmact_gfx    = "None"buildable    = FALSEhelptext     = ""; ----------------------------------------[extra_healer_charges_3]name         = _("Doctor used charge 3")rule_name    = "Healer_CHARGE_3"category     = "Bonus"graphic      = "extra.ruins"activity_gfx = "None"rmact_gfx    = "None"buildable    = FALSEhelptext     = ""; ----------------------------------------[extra_healer_charges_4]name         = _("Doctor used charge 4")rule_name    = "Healer_CHARGE_4"category     = "Bonus"graphic      = "extra.ruins"activity_gfx = "None"rmact_gfx    = "None"buildable    = FALSEhelptext     = ""; ----------------------------------------
c) The effects

Now open the file effects.ruleset.
Copy and paste all of it at the end of the file.
The first two effects are to control the movement after the action is successful.
Both units lose movement after successful action.
The last effect is to stipulate how much the unit will recover.
In this case 50%
And the last one is "User_Effect_1", which controls whether it can recharge

File: effects.ruleset

Code:

; ----------------------------------------; EFFECTS HEALER; ----------------------------------------[effect_action_success_heal_unit_actor]type  = "Action_Success_Actor_Move_Cost"value = 65535reqs  = {"type",     "name",      "range", "present""Action",   "Heal Unit", "Local", TRUE}; ----------------------------------------[effect_action_success_heal_unit_target]type  = "Action_Success_Target_Move_Cost"value = 65535reqs  = {"type",     "name",      "range", "present""Action",   "Heal Unit", "Local", TRUE}; ----------------------------------------[effect_action_heal_unit_recover]type  = "Heal_Unit_Pct"value = -50reqs  = {"type",     "name",      "range", "present""Action",   "Heal Unit", "Local", TRUE}; ----------------------------------------[effect_action_heal_unit_recharge]type  = "User_Effect_1"value = 1reqs  = {"type",         "name",            "range",   "present""UnitState",    "MovedThisTurn",   "Local",   FALSE"MinMoveFrags", "1",               "Local",   TRUE"Extra",        "Healer_CHARGE_1", "Local",   TRUE"UnitFlag",     "Healer",          "Local",   TRUE"CityTile",     "Center",          "Local",   TRUE"Building",     "Temple",          "City",    TRUE}; ----------------------------------------
d) The game

Now, in the game.ruleset file, we cannot place the following codes at the end of the file as we did before.
We need to put in specific places that will be detailed below.
This next script should be placed inside the [actions] property.
Add the following line right below the "Keep Moving" action

ui_name_heal_unit = _("%sHEAL THIS UNIT%s")

File: game.ruleset

Code:

.........; /* TRANS: Regular _Move (100% chance of success). */ui_name_unit_move = _("%sKeep moving%s")ui_name_heal_unit = _("%sHEAL THIS UNIT%s").........
And a few lines below, add the following lines, right before the action "Sabotage City"

File: game.ruleset

Code:

.........;; */ <-- avoid gettext warnings; ----------------------------------------; ACTION  HEAL UNIT; ----------------------------------------[actionenabler_heal_unit]action = "Heal Unit"actor_reqs    =    {"type",         "name",       "range", "present"     "UnitFlag",     "Healer",     "Local", TRUE     "MinMoveFrags", 1,            "Local", TRUE     "DiplRel",      "Armistice",  "Local", FALSE     "DiplRel",      "War",        "Local", FALSE     "DiplRel",      "Cease-fire", "Local", FALSE     "DiplRel",      "Peace",      "Local", FALSE     "DiplRel",      "Never met",  "Local", FALSE    }target_reqs  =    {"type",           "name",       "range", "present" "MaxUnitsOnTile", 1,            "Local", TRUE    }; ----------------------------------------[actionenabler_sabotage_city]action = "Sabotage City".........
e) The script

Last but not least, the LUA scripts.
Copy everything and paste it at the end of the script.lua file

File: script.lua

Code:

---------------------------------------------------------------------------------- Units Flags and the charging system.-- Place here the units that will use the charging system and how many charges each one has.-- Remember! Only units that also have the "Unique" flag.__flagsWithCharges = {['Healer'] = 5, -- Unit: The Doctor with 5 charges-- ['ReduceCrime'] = 3, -- Unit: The Marshall with 3 charges (EXAMPLE)-- ['Happiness'] = 2, -- Unit: The Clown with 2 charges (ANOTHER EXAMPLE)}---------------------------------------------------------------------------------- This script is adjusted for actions on units. -- If the action is in the city or on the tile, the call is similar to the one below--------------------------------------------------------------------------------function action_finished_unit_unit_callback(action, result, actor, target)if action:rule_name() == 'Heal Unit' and result == true thenmanage_charges(actor)endendsignal.connect('action_finished_unit_unit', 'action_finished_unit_unit_callback')--------------------------------------------------------------------------------function unit_built_callback(unit, city) local nmFlag = getFlagUnitByExtras(unit, __flagsWithCharges)if nmFlag ~= '' thennotify_charges_unit_built(unit, nmFlag)endreturn falseendsignal.connect('unit_built', 'unit_built_callback')--------------------------------------------------------------------------------function player_phase_begin_callback(player, new_phase) notify_charges_remaining_all_units(player)endsignal.connect('player_phase_begin', 'player_phase_begin_callback')--------------------------------------------------------------------------------function player_alive_phase_end_callback(player) manage_charges_recharge(player)endsignal.connect('player_alive_phase_end', 'player_alive_phase_end_callback')---------------------------------------------------------------------------------- CHARGING SYSTEM--------------------------------------------------------------------------------function manage_charges(actor)-- check if the unit use the charging systemlocal nmFlag = ''nmFlag = getFlagUnitByExtras(actor, __flagsWithCharges)-- if does, run the function 'manage_charges_actor'if nmFlag ~= '' thenlocal disbanded = manage_charges_actor(actor, nmFlag)-- if run out charges, mark its city to disband unitif disbanded == true thenactor:kill('disbanded', actor.owner)endendend--------------------------------------------------------------------------------function manage_charges_actor(actor, nmFlag)  local city = actor:get_homecity()local disbanded = falseif city thenlocal nmFlagFinal = ''local nmFlagFinalC = ''local nmFlagFinalT = ''local hasExtraC = falselocal hasExtraT = falselocal totalCharges = __flagsWithCharges[nmFlag]nmFlagFinal = nmFlag .. '_CHARGE_'nmFlagFinalC = nmFlagFinal .. '1'nmFlagFinalT = nmFlagFinal .. totalCharges - 1-- Is it the first charge or still rolling?hasExtraC = city.tile:has_extra(nmFlagFinalC) -- Is it in the end? (if it is, so it will be used and will run out)hasExtraT = city.tile:has_extra(nmFlagFinalT) if hasExtraC thenif hasExtraT then-- end of charges (remove all extras)rmvExtrasTile(nmFlagFinal, totalCharges, city.tile)notify_charges_run_out(actor)disbanded = trueelse-- pay one more charge (add one more extra)addExtrasTile(nmFlagFinal, totalCharges, city.tile)notify_charges_used(actor)endelse-- begin the counter (first charge used)city.tile:create_extra(nmFlagFinalC)notify_charges_first(actor)endif disbanded == false then-- show how many charges leftnotify_charges_show(actor, nmFlag, totalCharges)endendreturn disbandedend--------------------------------------------------------------------------------function manage_charges_recharge(player)local txtNacao = player.nation:name_translation()if txtNacao ~= "Animal Kingdom" and txtNacao ~= "Barbarian" and txtNacao ~= "Pirate" thenlocal unitfor unit in player:units_iterate() do-- check if the unit use the charging systemlocal nmFlag = getFlagUnitByExtras(unit, __flagsWithCharges)if nmFlag ~= '' thenlocal recharge = 0-- Check if the effect 'User_Effect_1' returns POSITIVE (1). -- This means that the unit is in the city with the Temple -- and has not moved this turnrecharge = effects.unit_bonus(unit, unit.owner, 'User_Effect_1')if recharge > 0 thenlocal city = unit:get_homecity()if city then-- remove all the extras from the homecitylocal totalCharges = __flagsWithCharges[nmFlag]rmvExtrasTile(nmFlag .. '_CHARGE_', totalCharges, city.tile)notify_charges_recharge(unit, nmFlag)endendendendendend--------------------------------------------------------------------------------function getFlagUnitByExtras(unit, extras)local nmFlaglocal totallocal nmFlag_final = ''for nmFlag, total in pairs(extras) doif unit.utype:has_flag(nmFlag) thennmFlag_final = nmFlagbreakendendreturn nmFlag_finalend--------------------------------------------------------------------------------function addExtrasTile(nmFlag, total, tile)local posfor pos = 1, total doif not tile:has_extra(nmFlag .. pos) thentile:create_extra(nmFlag .. pos) return posendendend--------------------------------------------------------------------------------function rmvExtrasTile(nmFlag, total, tile)local posfor pos = 1, total doif tile:has_extra(nmFlag.. pos) thentile:remove_extra(nmFlag .. pos) endendend--------------------------------------------------------------------------------function getChargesRemaining(actor, nmFlag, totalCharges)local pos = 0local total = 0local pos_final = 0local city = actor:get_homecity()if city thenfor pos = totalCharges, 1, -1 dolocal hasExtra = city.tile:has_extra(nmFlag .. '_CHARGE_' .. pos)if hasExtra thenpos_final = posbreakendendendtotal = totalCharges - pos_finalreturn totalend--------------------------------------------------------------------------------function notify_charges_remaining_all_units(player)local unitlocal citylocal totalCharges if player:is_human() thenfor unit in player:units_iterate() docity = unit:get_homecity()if city then for nmFlag, totalCharges in pairs(__flagsWithCharges) dolocal useChargeSystem = unit.utype:has_flag(nmFlag)if useChargeSystem == true thennotify_charges_show(unit, nmFlag, totalCharges)endendendendendend--------------------------------------------------------------------------------function notify_charges_run_out(actor)local msg = 'The unit %s exhausted its charges and was disbanded.'notify.event(actor.owner, actor.tile, E.UNIT_ACTION_ACTOR_SUCCESS, msg, actor:link_text())end--------------------------------------------------------------------------------function notify_charges_used(actor)local msg = 'The unit %s used a charge.'notify.event(actor.owner, actor.tile, E.UNIT_ACTION_ACTOR_SUCCESS, msg, actor:link_text())end--------------------------------------------------------------------------------function notify_charges_first(actor)local msg = 'The unit %s has just used its first charge.'notify.event(actor.owner, actor.tile, E.UNIT_ACTION_ACTOR_SUCCESS, msg, actor:link_text())end--------------------------------------------------------------------------------function notify_charges_last(actor)local msg = 'The unit %s has its LAST charge.'notify.event(actor.owner, actor.tile, E.UNIT_ACTION_ACTOR_SUCCESS, msg, actor:link_text())end--------------------------------------------------------------------------------function notify_charges_remaining(actor, charges)local msg = 'The unit %s has %d charge(s) left.'notify.event(actor.owner, actor.tile, E.UNIT_ACTION_ACTOR_SUCCESS, msg, actor:link_text(), charges)end--------------------------------------------------------------------------------function notify_charges_recharge(actor, nmFlag)local msg = 'The unit %s has recharged its charges. Now it has %d charge(s) left.'notify.event(actor.owner, actor.tile, E.UNIT_ACTION_ACTOR_SUCCESS, msg, actor:link_text(), __flagsWithCharges[nmFlag])end--------------------------------------------------------------------------------function notify_charges_unit_built(actor, nmFlag)local msg = 'The unit %s has just been built. It has %d charge(s).'notify.event(actor.owner, actor.tile, E.UNIT_ACTION_ACTOR_SUCCESS, msg, actor:link_text(), __flagsWithCharges[nmFlag])end--------------------------------------------------------------------------------function notify_charges_show(actor, nmFlag, totalCharges)local charges = getChargesRemaining(actor, nmFlag, totalCharges)if charges ~= totalCharges then if charges == 1 thennotify_charges_last(actor)endendif charges ~= 1 thennotify_charges_remaining(actor, charges)endend--------------------------------------------------------------------------------
Oh! About the Archaeologist. The script is easy, after remove the extra from tile, create a building "GreatWonder" in his hometown called "Display Fossil Mesozoic Era". Another building called "Display Weapons 10,000 years old" etc
So a made my own tileset, create a LOT of buildings, and grab a LOT of images from internet about artifacts, fossil, etc. Soooo cool!

I hope this helps someone
See ya

Statistics: Posted by leo.priori — Thu Apr 04, 2024 12:30 am



Viewing all articles
Browse latest Browse all 354

Trending Articles