Bay 12 Games Forum

Please login or register.

Login with username, password and session length
Advanced search  
Pages: [1] 2

Author Topic: [44.12] Night of the Werepumpkins! v1.1 (requires dfhack)  (Read 5500 times)

brolol.404

  • Bay Watcher
    • View Profile
[44.12] Night of the Werepumpkins! v1.1 (requires dfhack)
« on: October 19, 2019, 10:25:50 am »

Download: [44.12] Night of the Werepumpkins!
Download: Night of the Werepumpkins! - Optional Graphics Pack v1.0 (by Meph)



Once a year, when the Hunter's Moon looms over the horizon, a vile evil spreads across the land. From the darkness, the great werepumpkins emerge from their lairs and terrorize the world with their horde of werepumpkin minions.

This mod adds a Hunter's Moon (10th of Sandstone) event every year, where a hundred werepumpkins and their great werepumpkin master come to attack your fortress. Unlike other werecreatures, werepumpkins can infect all living creatures (intelligent or not) and do not transform based on the full moon. Once bitten, a creature will transform into and remain a werepumpkin until killed.

Now you will know why you fear the night!

Atmospheric Music (for the event)

I encourage any feedback or recommendations. Let me know if you like the idea of timed events like this. I think this concept has a lot of potential for a fortress defense type mod. Also, after testing this out, let me know if the event should have a minimum dwarf count to prevent it from taking place during the first year or if you enjoy watching your starting dwarves run in terror.


Happy Halloween!

Spoiler: Changelog v1.1 (click to show/hide)

« Last Edit: October 23, 2019, 09:50:23 am by brolol.404 »
Logged

Meph

  • Bay Watcher
    • View Profile
    • worldbicyclist
Re: [44.12] Night of the Werepumpkins! v1.0 (requires dfhack)
« Reply #2 on: October 20, 2019, 10:58:40 am »



Corpse overrides would work like:
[OVERRIDE:creature-tile:I:CORPSE:CORPSE::tileset-id:new-tile]

Your pumpkins have [CREATURE_TILE:'P'], which is tile 80. (112 for the lower case p) Since those are the only things you override, you'd need a new tileset for the corpses:



Override file would be something like:
Code: [Select]
[TILESET:_pumpkin.png:_pumpkin.png:10]
[OVERRIDE:80:I:CORPSE:CORPSE::10:1]
[OVERRIDE:112:I:CORPSE:CORPSE::10:0]
(Please mind that it will also override any other corpses of creatures using P or p.

Creature sprites:


Code: [Select]
graphics_pumpkin

[OBJECT:GRAPHICS]

[TILE_PAGE:PUMPKIN]
[FILE:pumpkin.png]
[TILE_DIM:32:32]
[PAGE_DIM:2:2]

[CREATURE_GRAPHICS:wp_WEREPUMPKIN_GREAT]
        [DEFAULT:PUMPKIN:0:0:AS_IS:DEFAULT]
[CHILD:PUMPKIN:0:0:AS_IS:DEFAULT]
[ANIMATED:PUMPKIN:1:0:AS_IS:DEFAULT]

[CREATURE_GRAPHICS:wp_WEREPUMPKIN]
        [DEFAULT:PUMPKIN:0:1:AS_IS:DEFAULT]
[CHILD:PUMPKIN:0:1:AS_IS:DEFAULT]
[ANIMATED:PUMPKIN:1:1:AS_IS:DEFAULT]

Hope it helps :)

PS: Have you tested their pathfinding, will they run towards the fort? Buildingdestroyer might help with that.
« Last Edit: October 20, 2019, 03:41:59 pm by Meph »
Logged
::: ☼Meph Tileset☼☼Map Tileset☼- 32x graphic sets with TWBT :::
::: ☼MASTERWORK DF☼ - A comprehensive mod pack now on Patreon - 250.000+ downloads and counting :::
::: WorldBicyclist.com - Follow my bike tours around the world - 148 countries visited :::

brolol.404

  • Bay Watcher
    • View Profile
Re: [44.12] Night of the Werepumpkins! v1.0 (requires dfhack)
« Reply #3 on: October 20, 2019, 05:13:34 pm »

I had the same idea, except you did it better

I thought that this would be a fun little project. Let me know your thoughts/feedback :)



Corpse overrides would work like:
[OVERRIDE:creature-tile:I:CORPSE:CORPSE::tileset-id:new-tile]

Your pumpkins have [CREATURE_TILE:'P'], which is tile 80. (112 for the lower case p) Since those are the only things you override, you'd need a new tileset for the corpses:

Override file would be something like:
Code: [Select]
[TILESET:_pumpkin.png:_pumpkin.png:10]
[OVERRIDE:80:I:CORPSE:CORPSE::10:1]
[OVERRIDE:112:I:CORPSE:CORPSE::10:0]
(Please mind that it will also override any other corpses of creatures using P or p.

Creature sprites:


Code: [Select]
graphics_pumpkin

[OBJECT:GRAPHICS]

[TILE_PAGE:PUMPKIN]
[FILE:pumpkin.png]
[TILE_DIM:32:32]
[PAGE_DIM:2:2]

[CREATURE_GRAPHICS:wp_WEREPUMPKIN_GREAT]
        [DEFAULT:PUMPKIN:0:0:AS_IS:DEFAULT]
[CHILD:PUMPKIN:0:0:AS_IS:DEFAULT]
[ANIMATED:PUMPKIN:1:0:AS_IS:DEFAULT]

[CREATURE_GRAPHICS:wp_WEREPUMPKIN]
        [DEFAULT:PUMPKIN:0:1:AS_IS:DEFAULT]
[CHILD:PUMPKIN:0:1:AS_IS:DEFAULT]
[ANIMATED:PUMPKIN:1:1:AS_IS:DEFAULT]

Hope it helps :)

These are really awesome! Thanks!

I added the following to the bottom of the overrides file:

Code: [Select]
[TILESET:wb_werepumpkin_corpse:wb_werepumpkin_corpse.png:10]
[OVERRIDE:80:I:CORPSE:CORPSE::10:1]
[OVERRIDE:112:I:CORPSE:CORPSE::10:0]

but am getting the following error:

Code: [Select]
TWBT: data/art/wb_werepumpkin_corpse not found
TWBT: no tileset with id 10
TWBT: invalid override specification OVERRIDE:80:I:CORPSE:CORPSE::10:1
TWBT: no tileset with id 10
TWBT: invalid override specification OVERRIDE:112:I:CORPSE:CORPSE::10:0

I am not sure why though because I named the .png sprite sheet "wb_werepumpkin_corpse" and put it in the data/art folder.

PS: Have you tested their pathfinding, will they run towards the fort? Buildingdestroyer might help with that.

The werepumpkins seemed to just hang out on the surface even with building destroyer in testing so I ended up giving them curious beast item. While not ideal, they now path to the most valuable items in your fort and will attack anyone in their way. The great pumpkin doesn't have this though. I could make him a necro and have him hang around bringing the dead back to life after the carnage though lol

Meph

  • Bay Watcher
    • View Profile
    • worldbicyclist
Re: [44.12] Night of the Werepumpkins! v1.0 (requires dfhack)
« Reply #4 on: October 20, 2019, 05:23:41 pm »

My mistake, there is a typo in the code I posted. [TILESET:wb_werepumpkin_corpse:wb_werepumpkin_corpse.png:10] should be [TILESET:wb_werepumpkin_corpse.png:wb_werepumpkin_corpse.png:10] A ".png" was missing.

You can make the great werepumpkin a necromancer that can only resurrect werepumpkins. Might even ramp up the range A LOT and remove line of sight, which means he'd animate pumpkins again and again, even while camping on the map edge. ;)
Logged
::: ☼Meph Tileset☼☼Map Tileset☼- 32x graphic sets with TWBT :::
::: ☼MASTERWORK DF☼ - A comprehensive mod pack now on Patreon - 250.000+ downloads and counting :::
::: WorldBicyclist.com - Follow my bike tours around the world - 148 countries visited :::

brolol.404

  • Bay Watcher
    • View Profile
Re: [44.12] Night of the Werepumpkins! v1.0 (requires dfhack)
« Reply #5 on: October 20, 2019, 05:48:48 pm »

My mistake, there is a typo in the code I posted. [TILESET:wb_werepumpkin_corpse:wb_werepumpkin_corpse.png:10] should be [TILESET:wb_werepumpkin_corpse.png:wb_werepumpkin_corpse.png:10] A ".png" was missing.

You can make the great werepumpkin a necromancer that can only resurrect werepumpkins. Might even ramp up the range A LOT and remove line of sight, which means he'd animate pumpkins again and again, even while camping on the map edge. ;)

Awesome that worked. The werepumpkin vines look like meat and bones when they get ripped off. Is there anyway to change that as well or would that require overwriting all corpse pieces in the game?

And that's a great idea :) I will work on that

Meph

  • Bay Watcher
    • View Profile
    • worldbicyclist
Re: [44.12] Night of the Werepumpkins! v1.0 (requires dfhack)
« Reply #6 on: October 20, 2019, 06:19:31 pm »

Yeah, you can override those individually.

Here the bodyparts and rotten bodyparts for a cat for example:
Code: [Select]
[OVERRIDE:224:I:ANY_REFUSE:MEAT::_MDF_overrides_13:3:16:::CREATURE_MAT:CAT:BRAIN]
[OVERRIDE:224:I:ANY_REFUSE:MEAT::_MDF_overrides_13:18:16:::CREATURE_MAT:CAT:MUSCLE] - meat
[OVERRIDE:224:I:ANY_REFUSE:MEAT::_MDF_overrides_13:2:16:::CREATURE_MAT:CAT:GUT]
[OVERRIDE:224:I:ANY_REFUSE:MEAT::_MDF_overrides_13:5:16:::CREATURE_MAT:CAT:HEART]
[OVERRIDE:224:I:ANY_REFUSE:MEAT::_MDF_overrides_13:4:16:::CREATURE_MAT:CAT:STOMACH]
[OVERRIDE:224:I:ANY_REFUSE:MEAT::_MDF_overrides_13:6:16:::CREATURE_MAT:CAT:KIDNEY]
[OVERRIDE:224:I:ANY_REFUSE:MEAT::_MDF_overrides_13:19:16:::CREATURE_MAT:CAT:LIVER]
[OVERRIDE:224:I:ANY_REFUSE:MEAT::_MDF_overrides_13:17:16:::CREATURE_MAT:CAT:SPLEEN]
[OVERRIDE:224:I:ANY_REFUSE:MEAT::_MDF_overrides_13:17:16:::CREATURE_MAT:CAT:PANCREAS]
[OVERRIDE:224:I:ANY_REFUSE:MEAT::_MDF_overrides_13:20:16:::CREATURE_MAT:CAT:LUNG]
[OVERRIDE:224:I:ANY_REFUSE:MEAT::_MDF_overrides_13:21:16:::CREATURE_MAT:CAT:GIZZARD]
[OVERRIDE:224:I:ANY_REFUSE:MEAT::_MDF_overrides_13:0:16:::CREATURE_MAT:CAT:EYE]

[OVERRIDE:224:I:MEAT:MEAT::_MDF_overrides_2:167::::CREATURE_MAT:CAT:BRAIN]
[OVERRIDE:224:I:MEAT:MEAT::_MDF_overrides_2:182::::CREATURE_MAT:CAT:MUSCLE] - meat
[OVERRIDE:224:I:MEAT:MEAT::_MDF_overrides_2:166::::CREATURE_MAT:CAT:GUT]
[OVERRIDE:224:I:MEAT:MEAT::_MDF_overrides_2:169::::CREATURE_MAT:CAT:HEART]
[OVERRIDE:224:I:MEAT:MEAT::_MDF_overrides_2:168::::CREATURE_MAT:CAT:STOMACH]
[OVERRIDE:224:I:MEAT:MEAT::_MDF_overrides_2:170::::CREATURE_MAT:CAT:KIDNEY]
[OVERRIDE:224:I:MEAT:MEAT::_MDF_overrides_2:171::::CREATURE_MAT:CAT:LIVER]
[OVERRIDE:224:I:MEAT:MEAT::_MDF_overrides_2:181::::CREATURE_MAT:CAT:SPLEEN]
[OVERRIDE:224:I:MEAT:MEAT::_MDF_overrides_2:181::::CREATURE_MAT:CAT:PANCREAS]
[OVERRIDE:224:I:MEAT:MEAT::_MDF_overrides_2:184::::CREATURE_MAT:CAT:LUNG]
[OVERRIDE:224:I:MEAT:MEAT::_MDF_overrides_2:185::::CREATURE_MAT:CAT:GIZZARD]
[OVERRIDE:224:I:MEAT:MEAT::_MDF_overrides_2:164::::CREATURE_MAT:CAT:EYE]

For the random bodyparts that are ripped off, you need [OVERRIDE:253:I:CORPSEPIECE:CORPSEPIECE:: yadda yadda pumpkin mat]
Logged
::: ☼Meph Tileset☼☼Map Tileset☼- 32x graphic sets with TWBT :::
::: ☼MASTERWORK DF☼ - A comprehensive mod pack now on Patreon - 250.000+ downloads and counting :::
::: WorldBicyclist.com - Follow my bike tours around the world - 148 countries visited :::

brolol.404

  • Bay Watcher
    • View Profile
Re: [44.12] Night of the Werepumpkins! v1.0 (requires dfhack)
« Reply #7 on: October 20, 2019, 06:45:41 pm »

I'm not sure I completely understand.

This is my creature code:

Code: [Select]
[CREATURE:wp_WEREPUMPKIN]
[DESCRIPTION:A large pumpkin with twisting vines. It has a large mouth and narrow eyes. Its eyes glow yellow.]
[NAME:werepumpkin:werepumpkins:werepumpkin]
[CASTE_NAME:werepumpkin:werepumpkins:werepumpkin]
[CREATURE_TILE:'p'][COLOR:6:0:1]
[GLOWTILE:'"'][GLOWCOLOR:6:0:1]
[SUPERNATURAL][FANCIFUL]
[DIFFICULTY:4]
[LARGE_PREDATOR][NIGHT_CREATURE_HUNTER]
[LAIR:WILDERNESS_LOCATION:30]
[GRASSTRAMPLE:0]
[NOT_LIVING]
[OPPOSED_TO_LIFE]
    [CANOPENDOORS]
[TRAPAVOID]
[CURIOUSBEAST_ITEM]
[NOEMOTION][NOTHOUGHT][NOT_BUTCHERABLE]
[NO_DRINK][NO_EAT][NO_SLEEP][NOBREATHE]
[NOPAIN][NOSTUN][NONAUSEA][NOFEAR][NOEXERT][NO_DIZZINESS][NO_FEVERS]
    [BIOME:ANY_LAND][FREQUENCY:1]
[POPULATION_NUMBER:20:45]
[CLUSTER_NUMBER:10:15]
[LOW_LIGHT_VISION:10000]
[ODOR_LEVEL:0] no smell
[SMELL_TRIGGER:10000] cannot smell
[NATURAL_SKILL:WRESTLING:10]
[NATURAL_SKILL:CLIMBING:10]
[NATURAL_SKILL:BITE:6]
[NATURAL_SKILL:GRASP_STRIKE:4]
[NATURAL_SKILL:MELEE_COMBAT:4]
[NATURAL_SKILL:DODGING:2]
[NATURAL_SKILL:SITUATIONAL_AWARENESS:6]
[NATURAL_SKILL:SNEAK:20]
[PHYS_ATT_RANGE:STRENGTH:1000:1150:1250:1500:2000:2250:2500]
[PHYS_ATT_RANGE:AGILITY:1000:1150:1250:1500:2000:2250:2500]
[PHYS_ATT_RANGE:TOUGHNESS:850:900:950:1000:1050:1100:1150]
[PHYS_ATT_RANGE:ENDURANCE:850:900:950:1000:1050:1100:1150]
[PHYS_ATT_RANGE:RECUPERATION:5000:5000:5000:5000:5000:5000:5000]
[PHYS_ATT_RANGE:DISEASE_RESISTANCE:700:1300:1400:1500:1600:1800:2500]
[MENT_ATT_RANGE:FOCUS:1250:1500:1750:2000:2500:3000:5000]
[NOBONES]
[EXTRAVISION]
[BODY:wp_WEREPUMPKIN:MOUTH]
[NO_THOUGHT_CENTER_FOR_MOVEMENT]
[USE_MATERIAL_TEMPLATE:wp_PUMPKIN_TISSUE_MAT:STRUCTURAL_PLANT_TEMPLATE]
[STATE_NAME:ALL_SOLID:pumpkin shell]
[STATE_ADJ:ALL_SOLID:pumpkin shell]
[STATE_NAME:LIQUID:melted pumpkin shell]
[STATE_ADJ:LIQUID:melted pumpkin shell]
[STATE_NAME:GAS:boiling pumpkin shell]
[STATE_ADJ:GAS:boiling pumpkin shell]
[PREFIX:pumpkin]
[STATE_COLOR:ALL:ORANGE]
[EDIBLE_VERMIN]
[EDIBLE_RAW]
[EDIBLE_COOKED]
[ROTS]
[USE_MATERIAL_TEMPLATE:wp_VINE_TISSUE_MAT:STRUCTURAL_PLANT_TEMPLATE]
[STATE_NAME:ALL_SOLID:pumpkin vine]
[STATE_ADJ:ALL_SOLID:pumpkin vine]
[STATE_NAME:LIQUID:melted pumpkin vine]
[STATE_ADJ:LIQUID:melted pumpkin vine]
[STATE_NAME:GAS:boiling pumpkin vine]
[STATE_ADJ:GAS:boiling pumpkin vine]
[STATE_COLOR:ALL:GREEN]
[TISSUE:wp_PUMPKIN_TISSUE]
[TISSUE_NAME:werepumpkin tissue:NP]
[TISSUE_MATERIAL:LOCAL_CREATURE_MAT:wp_PUMPKIN_TISSUE_MAT]
[MUSCULAR]
[FUNCTIONAL]
[STRUCTURAL]
[RELATIVE_THICKNESS:1]
[CONNECTS]
[TISSUE_SHAPE:LAYER]
[TISSUE:wp_VINE_TISSUE]
[TISSUE_NAME:werepumpkin vine tissue:NP]
[TISSUE_MATERIAL:LOCAL_CREATURE_MAT:wp_VINE_TISSUE_MAT]
[MUSCULAR]
[FUNCTIONAL]
[STRUCTURAL]
[RELATIVE_THICKNESS:1]
[CONNECTS]
[TISSUE_SHAPE:LAYER]
[TISSUE_LAYER:BY_CATEGORY:wp_HEAD:wp_PUMPKIN_TISSUE]
[TISSUE_LAYER:BY_CATEGORY:MOUTH:wp_PUMPKIN_TISSUE]
[TISSUE_LAYER:BY_CATEGORY:wp_VINE:wp_VINE_TISSUE]
[BODY_DETAIL_PLAN:STANDARD_HEAD_POSITIONS]
[BODY_SIZE:0:0:10000]
[ATTACK:BITE:BODYPART:BY_CATEGORY:MOUTH]
[ATTACK_SKILL:BITE]
[ATTACK_VERB:bite:bites]
[ATTACK_CONTACT_PERC:100]
[ATTACK_PENETRATION_PERC:100]
[ATTACK_FLAG_EDGE]
[ATTACK_PRIORITY:MAIN]
[ATTACK_FLAG_CANLATCH]
[SPECIALATTACK_INTERACTION:wp_WEREPUMPKIN_CURSE]
[ATTACK:PUNCH:BODYPART:BY_TYPE:GRASP]
[ATTACK_SKILL:GRASP_STRIKE]
[ATTACK_VERB:whip:whips]
[ATTACK_CONTACT_PERC:100]
[ATTACK_PREPARE_AND_RECOVER:3:3]
[ATTACK_FLAG_WITH]
[ATTACK_PRIORITY:MAIN]
[APPLY_CREATURE_VARIATION:STANDARD_CLIMBING_GAITS:2990:2257:1525:731:4300:6100] 12 kph
[APPLY_CREATURE_VARIATION:STANDARD_SWIMMING_GAITS:2990:2257:1525:731:4300:6100] 12 kph
[APPLY_CREATURE_VARIATION:STANDARD_CRAWLING_GAITS:2990:2257:1525:731:4300:6100] 12 kph
[SWIMS_INNATE]
[HOMEOTHERM:10040]
[NOCTURNAL]
[SPHERE:CHAOS]
[SPHERE:MOON]
[SPHERE:NIGHT]
[CASTE:MALE]
[NO_GENDER]
[SELECT_CASTE:ALL]
  [SELECT_TISSUE:BY_CATEGORY:wp_VINE:wp_VINE_TISSUE]
[TL_HEALING_RATE:20]

Is this correct then?

Code: [Select]
[TILESET:wp_werepumpkin_corpse.png:wp_werepumpkin_corpse.png:10]

[OVERRIDE:112:I:CORPSE:CORPSE::10:0]
[OVERRIDE:80:I:CORPSE:CORPSE::10:1]

[OVERRIDE:253:I:CORPSEPIECE:CORPSEPIECE::wp_werepumpkin_corpse:10:2:::CREATURE_MAT:wp_WEREPUMPKIN:wp_VINE]
[OVERRIDE:224:I:ANY_REFUSE:wp_VINE_TISSUE::wp_werepumpkin_corpse:10:2:::CREATURE_MAT:wp_WEREPUMPKIN:wp_VINE]
[OVERRIDE:253:I:CORPSEPIECE:CORPSEPIECE::wp_werepumpkin_corpse:10:2:::CREATURE_MAT:wp_WEREPUMPKIN_GREAT:wp_VINE]
[OVERRIDE:224:I:ANY_REFUSE:wp_VINE_TISSUE::wp_werepumpkin_corpse:10:2:::CREATURE_MAT:wp_WEREPUMPKIN_GREAT:wp_VINE]

[OVERRIDE:253:I:CORPSEPIECE:CORPSEPIECE::wp_werepumpkin_corpse:10:0:::CREATURE_MAT:wp_WEREPUMPKIN:wp_HEAD]
[OVERRIDE:224:I:ANY_REFUSE:wp_PUMPKIN_TISSUE::wp_werepumpkin_corpse:10:0:::CREATURE_MAT:wp_WEREPUMPKIN:wp_HEAD]
[OVERRIDE:253:I:CORPSEPIECE:CORPSEPIECE::wp_werepumpkin_corpse:10:1:::CREATURE_MAT:wp_WEREPUMPKIN_GREAT:wp_HEAD]
[OVERRIDE:224:I:ANY_REFUSE:wp_PUMPKIN_TISSUE::wp_werepumpkin_corpse:10:1:::CREATURE_MAT:wp_WEREPUMPKIN_GREAT:wp_HEAD]

This is my new sprite sheet:

wp_werepumpkin_corpse.png


EDIT: is there a way to use a unique overrides file by the way? (i.e. "overrides_wp")
« Last Edit: October 20, 2019, 08:10:01 pm by brolol.404 »
Logged

Meph

  • Bay Watcher
    • View Profile
    • worldbicyclist
Re: [44.12] Night of the Werepumpkins! v1.0 (requires dfhack)
« Reply #8 on: October 21, 2019, 03:34:02 am »

No unique override files, same problem as the onload.inits. ;)

Bodypart override:
[OVERRIDE:253:I:CORPSEPIECE:CORPSEPIECE::10:2::::CREATURE_MAT:wp_WEREPUMPKIN:wp_VINE]
Logged
::: ☼Meph Tileset☼☼Map Tileset☼- 32x graphic sets with TWBT :::
::: ☼MASTERWORK DF☼ - A comprehensive mod pack now on Patreon - 250.000+ downloads and counting :::
::: WorldBicyclist.com - Follow my bike tours around the world - 148 countries visited :::

brolol.404

  • Bay Watcher
    • View Profile
Re: [44.12] Night of the Werepumpkins! v1.0 (requires dfhack)
« Reply #9 on: October 21, 2019, 01:18:37 pm »

No unique override files, same problem as the onload.inits. ;)

Bodypart override:
[OVERRIDE:253:I:CORPSEPIECE:CORPSEPIECE::10:2::::CREATURE_MAT:wp_WEREPUMPKIN:wp_VINE]

Thanks. I will try this out :)

You can actually do unique onload.init files. OnLoad_wp.init is a valid file.
« Last Edit: October 21, 2019, 01:24:23 pm by brolol.404 »
Logged

brolol.404

  • Bay Watcher
    • View Profile
Re: [44.12] Night of the Werepumpkins! v1.0 (requires dfhack)
« Reply #10 on: October 21, 2019, 01:20:37 pm »

I was asked to put some comments in the script for this mod so that others could use it to make pre-determined random encounters and custom sieges. Well, I started on that, but realized that it made the code file too much of a mess, so I instead wrote a read me tutorial that I will include in the next update to this mod. Here is a copy of it.

I have learned a lot from Roses and Fleeting Frames. They have made this possible.

Code: [Select]
This script counts down to a specific date, displays a pop-up window and then finds
a random surface location and spawns a creature at that location a set number of times.

I originally planned for this to be done in one event, as one function
"function wp_moon_hunters()" that displayed a pop-up window and spawned 100 creatures
at random locations around the surface of the map. However, running multiple lines of
code (in this case thousands) at the same time crashes the game. So, instead this script
is done using multiple spawning wave functions. Each wave is separated by a day (1200 ticks)
and consists of 20 creatures; however, these numbers could be manipulated for more variety.

The countdown function is the countdown to the event. Note that it is not called until the
last line of the script file (after everything is read). It calls each event wave at the date
specified by finding the difference between the current date and the specified event date
and then using "dfhack.timeout(ticks+1,'ticks',FUNCTION_NAME)", like so:

   local ticks = date_moon_hunters_START - current_tick

The total ticks in a year is calculated with the following equation. This variable never changes:

   local year_tick_total = 1200*28*12

   This is equivalent to 403200 (1200 ticks in a day * 28 days in a month * 12 months in a year)

The current tick is calculated using:

   local current_tick = df.global.cur_year_tick

The specific event date is calculated using:

   local date_moon_hunters_START = 1200*(7*28+9)
   
   This is (1200 ticks in a day * (# of months * 28 days in a month * # of days)

   The # of months starts at Granite (not January) and the # of days is up to the date (the day
   before the day of the event)

Knowing this, we can do some interesting things here. Using a random number, we can now choose
a random date (within a date range) and not just use a specific date. Lets say that we want this
event to happen between June to September. We can now roll a four sided die and set the result
to the month, like so:

   local m = math.random(4) + 3
   local date_of_event = 1200*(m*28+9)

   This will choose a random number between 1 and 4 and then add 3 to it. Assuming we got a
   random number of 2, this event would happen on the 10th of July. We can further complicate
   this date like so:

   local m = math.random(4) + 3
   local d = math.random(28) - 1
   local date_of_event = 1200*(m*28+d)

   This event will now happen sometime between the 1st of June and the 28th of September.
   Remember that the random number will be between 1 and 28 and the # of days is the day before
   the event. Assuming we got the random numbers 3 and 12, this event would happen on August 12th.

   Note that these variables would change everytime the game was loaded.

   You can do the same thing with years in the future as well. This code here says that if the
   difference between the current date and the specified event date is less than 0, the event
   has already passed this year and will take place next year.

   if ticks < 0 then
    ticks = ticks + year_tick_total
   end

   By multiplying another random number to year_tick_total, you can have this event happen
   multiple years in the future. Once again, this would change everytime the game was loaded.

   local y = math.random(5)
   if ticks < 0 then
    ticks = ticks + year_tick_total * y
   end

The wave functions make up the actual actions of the events. For this script, they display a
pop-up notification or spawn a creature at a random location, but could do anything that you
want to happen in the event.

   The pop-up notification is as follows. Note that this does not pause the game. I also read
   that it should come after a creature is spawned, but I have not noticed any issue with that.
   If you are using a pop-up notification and a creature spawn at the same time, I suggest
   that you put the pop-up notification at the end:

   dfhack.gui.showPopupAnnouncement("announcement", COLOR_WHITE , true )

   The creature spawn is a simple dfhack.run_command(). Note that this only accepts a string.
   If you want to pass a variable that isn't a string, you have to convert it into a string
   first. The "modtools/create-unit" has ambush tags, but you will get a pop-up notification
   for every creature that is spotted. So, if you are spawning 100 creatures, that is 100
   notifications. I suggest that you use your own pop-up notification if you want to announce
   an ambush and are using multiple creatures.

   The actual spawn loop could be done in a for loop (and is probably the correct format), but
   I like while loops, so that is what I used. You can spawn a random number of creatures here:

   local n = 0
   local t = math.random (11) + 9
   while n < t do
    dfhack.run_command("modtools/create-unit")
    n = n + 1
   end
 
   This will spawn anywhere from 10 to 20 creatures. You can also set a percentage chance trigger
   here for anything to even happen:

   local event_chance = math.random(10)
   if event_chance == 1 then
    local n = 0
    local t = math.random (11) + 9
    while n < t do
     dfhack.run_command("modtools/create-unit")
     n = n + 1
    end
   end

   This means that the event only has a 10% chance of happening. You can change this percentage
   chance by changing the equation:

   local event_chance = math.random(10)
   if event_chance < 5 then
    local n = 0
    local t = math.random (11) + 9
    while n < t do
     dfhack.run_command("modtools/create-unit")
     n = n + 1
    end
   end

   This means that the event now has a 40% chance of happening. You can further complicate
   this by setting different chances to different amount of creature spawns.

   local event_chance = math.random(10)
   if event_chance < 5 then
    local n = 0
    local t = math.random (11) + 9
    while n < t do
     dfhack.run_command("modtools/create-unit")
     n = n + 1
    end
   else
    local n = 0
    local t = math.random (5)
    while n < t do
     dfhack.run_command("modtools/create-unit")
     n = n + 1
    end
   end

   Now this means that the event will happen. However, there is a 40% chance that 10 to
   20 creatures will spawn and a 60% chance that 1 to 5 creatures will spawn. To complicate
   this further, let's remove the event guarantee:

   local event_chance = math.random(10)
   if event_chance < 5 then
    local n = 0
    local t = math.random (11) + 9
    while n < t do
     dfhack.run_command("modtools/create-unit")
     n = n + 1
    end
   elseif event_chance == 5 or event_chance == 6
    local n = 0
    local t = math.random (5)
    while n < t do
     dfhack.run_command("modtools/create-unit")
     n = n + 1
    end
   else
    -- do nothing
   end

   Now there is a 40% chance that 10 to 20 creatures will spawn, a 20% chance that 1 to 5
   creatures will spawn and a 40% chance that the event will not happen.

   You can also add in population requirements, like so:

   local k = 0
   local event_chance = math.random(10)
   for i,unit in pairs(df.global.world.units.active) do
    if dfhack.units.isDwarf(unit) then
     k = k + 1
    end
   end
   if k > 79 then
    if event_chance < 5 then
     local n = 0
     local t = math.random (11) + 9
     while n < t do
      dfhack.run_command("modtools/create-unit")
      n = n + 1
     end
    else
     local n = 0
     local t = math.random (5)
     while n < t do
      dfhack.run_command("modtools/create-unit")
      n = n + 1
     end
    end
   end

   This code counts all of the dwarves on the map and then requires at least a count of 80
   for the event to happen. If there are 80 dwaves, there is a 40% chance that 10 to 20
   creatures will spawn, a 60% chance that 1 to 5 creatures will spawn and a 40% chance that
   the event will not happen.

   Once again, you can add an else/elseif to the population minimum if statement to make
   a fewer number (or a different creature(s)) spawn depending on the current population:

   local k = 0
   for i,unit in pairs(df.global.world.units.active) do
    if dfhack.units.isDwarf(unit) then
     k = k + 1
    end
   end
   if k > 79 then
    -- spawn 50 to 100 creatures
   else if k > 49 then
    -- spawn 20 to 50 creatures
   else if k > 19 then
    -- spawn 5 to 10 creatures
   else
    -- do nothing
   end

I hope this helps with your own events, pre-determined random encounters and custom sieges!

Let me know if you have any questions.


EDIT: I wanted to add one more tip to this write up:

Code: [Select]
I highly suggest using the print output while testing your script. You should put a
print output at the start of each function and for each important variable.

For example, this line of code will tell you the date of the event (in ticks), the current
tick and the difference between the two. This is a good way to find out when the event
should happen (e.g. 100 ticks from now or 10,000):

print("event tick:", date_event, "current tick:", current_tick, "ticks to event:", ticks)

This is also a good way to see what random number was chosen so that you can make
sure that the code is working as intended:

local n = math.random(5) + 3
printf(n)

« Last Edit: October 21, 2019, 05:06:56 pm by brolol.404 »
Logged

brolol.404

  • Bay Watcher
    • View Profile
Re: [44.12] Night of the Werepumpkins! v1.0 (requires dfhack)
« Reply #11 on: October 22, 2019, 10:49:45 am »

I realized that my code could be optimized. So, here is an update.

Code: [Select]
As described above, the following code should work fine:

   local event_chance = math.random(10)
   if event_chance < 5 then
    local n = 0
    local t = math.random (11) + 9
    while n < t do
     dfhack.run_command("modtools/create-unit")
     n = n + 1
    end
   elseif event_chance == 5 or event_chance == 6
    local n = 0
    local t = math.random (5)
    while n < t do
     dfhack.run_command("modtools/create-unit")
     n = n + 1
    end
   else
    -- do nothing
   end

This code gives a 40% chance that 10 to 20 creatures will spawn, a 20% chance
that 1 to 5 creatures will spawn and a 40% chance that the event will not happen.
However, this code could potentially get very large depending on how many actions
you have in the loops. This should do the same thing, but reduce the amount of
code required to be read. I suggest using one loop for each wave and instead
turning features on and off using variables, like so:

   local n = 0
   local event_chance = math.random(10)
   if event_chance < 5 then
    local t = math.random (11) + 9
   elseif event_chance == 5 or event_chance == 6
    local t = math.random (5)
   else
    local t = 0
   end
    while n < t do
     dfhack.run_command("modtools/create-unit")
     n = n + 1
    end

Fleeting Frames

  • Bay Watcher
  • Spooky cart at distance
    • View Profile
Re: [44.12] Night of the Werepumpkins! v1.0 (requires dfhack)
« Reply #12 on: October 23, 2019, 04:35:37 am »

Few notes on corpse overrides/creature tiles: As a matter of fact, I override all corpses (to change the letter from full-background to partial). Two useful notes about it in particular:

One, no creature in vanilla game uses 'Z' or non-letter tile such as ø (...wait, DF doesn't use ø either).

Two, I had to bolt in a patch to twbt for it to override sentient creature corpses, and then another patch for it to not apply those overrides when a creature with sentient creature tile (actually,nah, I just used all of them) walks over the tile. But I'm on older edited version; maybe mifki changed run order and it works fine in latest twbt?


As for the tutorial, seems good; there isn't much that explains the implications of code attached to the code. I mostly had to make do via docs, looking at other scripts that did something like I wanted and just trying and seeing what happens; dfhack scripts and plugins rarely include comments throughout it (very slightly smaller DL size though). Few notes, though they're not really about the tutorial as such...

• I had no idea you planned to spawn that many units, though. Yeah, that is crashy; but since you already iterate over a list you can just add a small timeout based on that number to spread the 10-20 out throughout the day so that only one spawnunit is run at time. For example:

Code: [Select]
   local n = 0
   local t = math.random (11) + 9
   while n < t do
    dfhack.timeout(n*59, 'ticks', function()
     dfhack.run_command("modtools/create-unit")
    end)
    n = n + 1
   end


‣I'll note that if you have an event happening "somewhere in season", and player saves and quits in the middle of each season, it's 50-50 odds of missing it for that year completely, as it gets placed for next year.

This prevents save-scumming, but it also causes missing it entirely.

Usually, if you want to keep the general date same, you could save it to save via persistent data/something else linked to save or some file. But here, there is a simpler way: you could simply seed a random number generator with current year (ensuring it generates same on every call for given year), one of the world-dependent seeds (ensure it generates different on near every world), and df.global.ui.site_id (ensure it generates at different date on different sites). Then maybe add another, smaller random number with random seed to have small variances in coming time even on save reload.

⁕isDwarf mostly works, but might have issues when modded, or there is a caravan or lot of visitors/residents or invaders or dead or whatnot. isAlive and isOwnCiv and isOwnGroup may work better.

‼ Beautiful bouncing pumpkin and the images in general!
« Last Edit: October 23, 2019, 04:40:23 am by Fleeting Frames »
Logged

brolol.404

  • Bay Watcher
    • View Profile
Re: [44.12] Night of the Werepumpkins! v1.1 (requires dfhack)
« Reply #13 on: October 23, 2019, 09:56:34 am »

Version 1.1 Released!
Optional Graphics Pack v1.0 (by Meph) Released!

Spoiler: Changelog v1.1 (click to show/hide)

Optional Graphics created by Meph!



brolol.404

  • Bay Watcher
    • View Profile
Re: [44.12] Night of the Werepumpkins! v1.0 (requires dfhack)
« Reply #14 on: October 23, 2019, 11:09:00 am »

One, no creature in vanilla game uses 'Z' or non-letter tile such as ø (...wait, DF doesn't use ø either).

This is a good tip, thanks. I will plan to change the werepumpkin default creature tile to a non-letter tile next update.

I had no idea you planned to spawn that many units, though. Yeah, that is crashy; but since you already iterate over a list you can just add a small timeout based on that number to spread the 10-20 out throughout the day so that only one spawnunit is run at time. For example:

Code: [Select]
   local n = 0
   local t = math.random (11) + 9
   while n < t do
    dfhack.timeout(n*59, 'ticks', function()
     dfhack.run_command("modtools/create-unit")
    end)
    n = n + 1
   end

Yeah, I got a little carried away lol

This is interesting. I didn't think about adding a wave variable to the timeout command. I could probably really shorten my werepumpkin code by creating one:

Code: [Select]
local current_tick = df.global.cur_year_tick
local date_moon_hunters = 1200*(7*28+9)
local ticks = date_moon_hunters - current_tick
local wave = 0
while wave < 5*1200 do
 if ticks < 0 then
  ticks = ticks + year_tick_total
  wave = 5*1200
 else
  dfhack.timeout(ticks+1+wave,'ticks',wp_moon_hunters)
  wave = wave + 1200
 end
end

function wp_moon_hunters()
 local n=0
 while n < 20 do
  pos = getPositionSurface()
  dfhack.run_command("modtools/create-unit -race wp_WEREPUMPKIN -caste MALE -location " .. '[ ' ..tostring(pos.x)..' '..tostring(pos.y)..' '..tostring(pos.z).. ' ]')
  dfhack.gui.refreshSidebar()
  n = n + 1
 end
end

you could simply seed a random number generator with current year (ensuring it generates same on every call for given year), one of the world-dependent seeds (ensure it generates different on near every world), and df.global.ui.site_id (ensure it generates at different date on different sites). Then maybe add another, smaller random number with random seed to have small variances in coming time even on save reload.

This is also very interesting. I will definitely look into this. Is there already a df variable that generates a random but consistent number to each world/site/save?

isDwarf mostly works, but might have issues when modded, or there is a caravan or lot of visitors/residents or invaders or dead or whatnot. isAlive and isOwnCiv and isOwnGroup may work better.

This is a good point, thanks. I'll update the tutorial to: dfhack.units.isOwnGroup(unit)
Pages: [1] 2