Tutorial - MemeMayhem/ModExamples GitHub Wiki
This is a step-by-step guide to help you create your first Meme Mayhem mod.
Every Meme Mayhem mod is stored in a folder that contains data files, image files, and script files. To create a new mod, simply create a new folder under %USERPROFILE%\AppData\LocalLow\Cr3 Studio\Meme Mayhem\Mods
(if you are on Linux, e.g., Steam Deck, use the path mentioned in this issue) and add the necessary files. If the Mods
folder does not exist, create it first.
In this tutorial, we will name our mod "First Mod". To create it, we need to create a new folder called First Mod
and add a manifest.json
file with the following content:
{
"name": "First Mod",
"description": "This is our first Meme Mayhem Mod."
}
Create/find a preview image for your mod, save it as thumbnail.png
in the mod folder. This image will be displayed on the mod's front page when published to the Steam Workshop.
The mod folder now contains the following files:
Meme Mayhem\Mods
+ First Mod
+ manifest.json
+ thumbnail.png
That's it. Our mod is ready to be loaded by the game. It doesn't have any functionality yet, but we will address that later on.
Restart the game and open the mod manager UI by pressing "Ctrl + F8". Our First Mod
should be listed in the UI, and you can view the mod folder by clicking the "View files" button.
If the mod manager does not list your mod, click the "Open local mods folder" button and check if your mod is placed in the right location. After correcting any errors, restart the game to reload the mods. After the mod is correctly loaded by the mod manager, you can use Ctrl + F5
to reload mods after making script changes. This is faster than restarting the game but only works after your mod is correctly loaded.
To make the mod functional, you need to add a mod script. The mod script is written in Lua and defines all features of the mod.
Under the mod folder, create a text file at the path additions\scripts\Trigger.lua.txt
with the following content:
MOD_MANAGER:AddMod(function(Api)
Api:CopyCharacter("naysayer", {
id = "my_character",
display_name = "First Mod Character",
short_description = "This is a character created by my first mod.",
})
end)
The script registers the mod by calling MOD_MANAGER:AddMod
and adds a new character in its implementation function. The new character is a copy of the existing "naysayer" character but with a different name.
Reload mods by pressing Ctrl + F5
in the game, and click "Play Mods". You should see our "First Mod Character" character as a selectable option.
The character still looks and plays the same as "naysayer". Next, we will customize it to make it a unique character.
To customize the looks of your character, you need to add images to your mod.
Under the mod folder, create an image folder at the path additions\textures
and add image files to it. It's recommended to only use lower-case letters and '_' for the file name. PNG and JPG files are supported.
Update the mod script to replace the character skin with these images:
MOD_MANAGER:AddMod(function(Api)
Api:CopyCharacter("naysayer", {
id = "my_character",
display_name = "First Mod Character",
short_description = "This is a character created by my first mod.",
+ icon = DCEI.Texture("my_character_small"),
+ icon_low_resolution = DCEI.Texture("my_character_small"),
+ icon_high_resolution = DCEI.Texture("my_character_large"),
})
end)
Two image files are added: my_character_small.png
and my_character_large.png
. Note that file extensions are omitted when they are referenced in Lua.
Reload mods with Ctrl + F5
and your character will now have a new look:
However, if you start a battle, you will notice the character will still look the same as before:
To customize the character's look in battle, we will need to add 2 JSON file at the path additions\data\Unit.json
and additions\data\Actor.json
:
The content of additions\data\Unit.json
:
{
"units": {
"COMBAT Unit First Mod Character":
{
"baseUnit": "_COMBAT Missile"
}
}
}
The content of additions\data\Actor.json
:
{
"actors": {
"COMBAT Unit First Mod Character" : {
"parent": "_COMBAT Unit Mob",
"unitActor": {
"resource": {
"name": "my_character_large"
},
"modelScale": 0.5
}
}
}
}
Then update the mod script to reference the new data:
MOD_MANAGER:AddMod(function(Api)
Api:CopyCharacter("naysayer", {
id = "my_character",
display_name = "First Mod Character",
short_description = "This is a character created by my first mod.",
icon = DCEI.Texture("my_character_small"),
icon_low_resolution = DCEI.Texture("my_character_small"),
icon_high_resolution = DCEI.Texture("my_character_large"),
+ unit = DCEI.Unit("COMBAT Unit First Mod Character"),
})
end)
Now the character in battle will also have the new look:
In Meme Mayhem, a character's primary weapon is emojis. You can add emojis to give your character special attacks.
Add an image file my_power_rock.png
for your emoji to additions\textures
and update the mod script to register the new emoji:
MOD_MANAGER:AddMod(function(Api)
+ Api:RegisterMissile("my_power_rock", {
+ id = "my_power_rock",
+ display_name = "My Powerful Rock",
+ icon = DCEI.Texture("my_power_rock"),
+ damage = function(attack_data, caster)
+ return 200
+ end
+ })
Api:CopyCharacter("naysayer", {
id = "my_character",
display_name = "First Mod Character",
short_description = "This is a character created by my first mod.",
icon = DCEI.Texture("my_character_small"),
icon_low_resolution = DCEI.Texture("my_character_small"),
icon_high_resolution = DCEI.Texture("my_character_large"),
unit = DCEI.Unit("COMBAT Unit First Mod Character"),
+ attack_ids = {
+ "my_power_rock",
+ "my_power_rock",
+ },
})
end)
The updated mod script introduces a new emoji named "My Powerful Rock", which inflicts significant damage per hit. This emoji is also set as the default in the starting emoji collection for our mod character. You can find a list of all available emoji missiles here: Missiles
Reload mods with Ctrl + F5
and play the mod character:
The character in battle still throws regular rocks instead of our upgraded one. Similar to characters, we need to add some extra data.
Add the following to additions/data/Unit.json
:
{
"units": {
+ "COMBAT Missile My Powerful Rock":
+ {
+ "baseUnit": "_COMBAT Missile"
+ },
"COMBAT Unit First Mod Character":
{
"baseUnit": "_COMBAT Missile"
}
}
}
And add the following to additions/data/Actor.json
:
"actors": {
+ "COMBAT Missile My Powerful Rock": {
+ "parent": "_COMBAT Missile Simple",
+ "unitActor": {
+ "resource": {
+ "name": "my_power_rock"
+ },
+ "modelScale": 2
+ }
+ },
"COMBAT Unit First Mod Character" : {
Then update the mod script to reference the added data:
Api:RegisterMissile("my_power_rock", {
id = "my_power_rock",
display_name = "My Powerful Rock",
icon = DCEI.Texture("my_power_rock"),
+ missile = DCEI.SimpleUnit("COMBAT Missile My Powerful Rock"),
damage = function(attack_data, caster)
return 100
end
})
Now we are throwing big black rocks at our opponent:
To make it more impactful, you can change the sound effect by adding a .wav file to additions/sounds
and reference it in the mod script:
Api:RegisterMissile("my_power_rock", {
id = "my_power_rock",
display_name = "My Powerful Rock",
icon = DCEI.Texture("my_power_rock"),
missile = DCEI.SimpleUnit("COMBAT Missile My Powerful Rock"),
+ sounds = {
+ DCEI.Sound("my_power_rock_hit")
+ },
damage = function(attack_data, caster)
return 200
end
})
While powerful, you will only have a fixed amount with a fixed damage and that won't get you very far. To make it a viable option, you will need to add perks and relics to work with the emoji.
After beating each enemy in Meme Mayhem, the player gets to select a perk from 3 random perk options. To add more "My Powerful Rock" to your collection, you need to create a perk that awards these emojis.
To create a new perk, add the following in the mod script:
+ Api:RegisterPerk("my_power_perk", {
+ id = "my_power_perk",
+ display_name = "My Powerful Perk",
+ description = "Adds one My Powerful Rock.",
+ icon = DCEI.Texture("my_power_rock"),
+ perk_type = "missile",
+ rarity = "legendary",
+ attacks = {
+ my_power_rock = 1,
+ },
+ })
Api:CopyCharacter("naysayer", {
id = "my_character",
display_name = "First Mod Character",
short_description = "This is a character created by my first mod.",
icon = DCEI.Texture("my_character_small"),
icon_low_resolution = DCEI.Texture("my_character_small"),
icon_high_resolution = DCEI.Texture("my_character_large"),
unit = DCEI.Unit("COMBAT Unit First Mod Character"),
attack_ids = {
"my_power_rock",
"my_power_rock",
},
+ perk_chains = {
+ {
+ "my_power_perk",
+ },
+ {
+ "gain_attack",
+ "attack_quest",
+ "damage_to_health",
+ "flex_on_flex",
+ },
+ },
Note that you need to add at least 4 perks for the character or the game won't work. Usually you want to have way more to enable different strategies. A complete list of built-in perks can be found at Perks.
Now you will be able to add more "My Powerful Rock" by selecting "My Powerful Perk":
Besides modifying character attributes and awarding emojis, perks can have more complex behavior by defining Lua functions that are run on certain events.
Here we add a new perk that makes our "My Powerful Rock" even stronger with a behavior function:
Api:RegisterPerk("small_rock_on_power_rock", {
id = "small_rock_on_power_rock",
display_name = "Rock in Rock",
description = "Throws a regular rock when My Powerful Rock hits its target.",
icon = DCEI.Texture("my_power_rock"),
perk_type = "perk",
rarity = "common",
}, function(combat_unit)
local name = "small_rock_on_power_rock"
combat_unit.Attack:RegisterOnMissileImpactCallback(name, function(level, attack_data, caster, target)
if attack_data.missile_id == "my_power_rock" then
for i = 1, level do
combat_unit.Attack:NewMissileAttack(target, "attack_rock")
end
end
end)
end)
This new perk registers a Lua function for on-missile-impact events. When a power rock thrown by the character hits its target, the character will throw a regular rock.
Don't forget to add this perk to the character's perk_chains
to make it available in the random selection pool:
perk_chains = {
{
"my_power_perk",
+ "small_rock_on_power_rock",
},
{
"gain_attack",
"attack_quest",
"damage_to_health",
"flex_on_flex",
},
},
Relics are very similar to perks in what they can do. The main difference lies in how they are acquired in the game. Perks are picked from 3 options before each battle, whereas relics are brought along with the character or picked from 2 options after a boss battle.
The following code defines a relic and adds it to our character's initial relic set:
+ Api:RegisterRelic("my_power_cookie", {
+ id = "my_power_cookie",
+ display_name = "Overpowered Cookie",
+ description = "When hit by any emoji, gain 1 " .. Api.GameMechanicTags.TAG.attack .. ". Attack speed is reduced by 50%.",
+ icon = DCEI.Texture("smh_cookie"),
+ rarity = "permanent",
+ modify_attributes = {
+ attack_speed = -0.5,
+ },
+ }, function(combat_unit)
+ local name = "my_power_cookie"
+ combat_unit.Attack:RegisterOnMissileHitCallback(name, function(level, attack_data, caster, target)
+ for i = 1, level do
+ combat_unit:ModifyAttribute("attack", 1, false)
+ end
+ end)
+ end)
Api:CopyCharacter("naysayer", {
id = "my_character",
display_name = "First Mod Character",
short_description = "This is a character created by my first mod.",
icon = DCEI.Texture("my_character_small"),
icon_low_resolution = DCEI.Texture("my_character_small"),
icon_high_resolution = DCEI.Texture("my_character_large"),
unit = DCEI.Unit("COMBAT Unit First Mod Character"),
+ relics = {
+ "my_power_cookie",
+ },
A complete list of built-in relics can be found at Relics.
With emojis, perks, and relics, we now have a complete (albeit simple) character.
If you want to add the relic to relic pool to make it appear in shop or boss drop, you can set the forth parameter of RegisterRelic
to true
For example, Api:RegisterRelic(name, config, callback, true)
To add a custom boss to your mod, register a new boss with the following change:
Api:RegisterBoss({
id = "my_boss",
display_name = "My Boss",
unit = DCEI.Unit("COMBAT Unit First Mod Character"),
icon = DCEI.Texture("my_character_small"),
icon_high_resolution = DCEI.Texture("my_character_large"),
cosmetic_data = {
ultimate_id = "ultimate_basketball",
},
attributes = {
health_maximum = 10000,
attack = 40
},
perks = {
attack_rock = 30,
attack_speed = 2,
},
lines = {
battle_start = {
{
"Hey, prepare to be crushed!",
"You're no match for me!",
}
},
on_start = {
{
"Let the battle begin!",
"Time to show you what I'm made of!",
}
},
on_ultimate = {
{
"Witness my ultimate power!",
"Feel the wrath of my ultimate attack!",
}
},
on_win = {
{
"Haha, victory is mine!",
"You never stood a chance!",
}
},
on_lose = {
{
"I'll get my revenge next time!",
"This is just a temporary setback!",
}
},
}
})
A boss is similar to a character and has many of the same attributes. For each boss, you also need to add some lines that will popup as text bubbles when the battle starts/ends. To make it simple, the same image from our mod character is used for the boss.
The registered boss won't appear in the campaign yet. We need to update the campaign boss list to include our custom boss:
Api:SetCampaignBossPool("my_character", {
"my_boss",
"miniboss_pop_kat",
"miniboss_kideo",
"miniboss_stone",
"miniboss_zuck",
"my_boss",
})
Here you need to specify exactly 6 bosses. We reuse existing bosses for the middle 4 and our custom boss is the first and last. A complete list of built-in bosses can be found at Bosses.
Reload mods with Ctrl + F5
and you will be able to fight against your custom boss.
Events randomly happen from time to time in a game run. You need to add an image my_choice_dog.png
to additions/textures
, then can add custom events:
Api:RegisterChoice({
id = "my_choice",
display_name = "Dog",
image = DCEI.Texture("my_choice_dog"),
description = "A dog is running towards you and looks like it's going to bite you. What do you do?",
options = {
{
flavor = "run away",
description = "+200 " .. Api.GameMechanicTags.TAG.health,
aftermath_narrative = "You escaped from the dog!",
aftermath_description = "You haven't run that fast in years and it feels great. +200 " .. Api.GameMechanicTags.TAG.health,
modify_attributes = {
health_maximum = 200,
},
},
{
flavor = "throw a rock at it",
description = "+1 " .. Api.GameMechanicTags.ATTACK_ICON.rock .. ", -100 " .. Api.GameMechanicTags.TAG.health,
aftermath_narrative = "You picked up a rock but the dog bit you before you could throw it.",
aftermath_description = "It hurts but at least you have a " .. Api.GameMechanicTags.ATTACK_ICON.rock .. " ready for the next dog encounter.",
modify_attributes = {
health_maximum = -100,
},
gain_relics = {
single_rock = 1,
},
}
}
})
This code registers a new choice event with two options. Each option has a flavor text, description, and aftermath narrative. You can modify attributes or gain relics based on the chosen option.
Once you have added the event, you can include it in your campaign by updating the campaign event list:
Api:SetChoicePool("my_character", {
"my_choice",
})
Reload mods with Ctrl + F5
and the event will be randomly triggered during the campaign.
Remember to customize the event details and options to fit your mod's theme and gameplay.
A complete list of built-in choices can be found at Chocies.
Advanced
You can custom the choice to only appear in certain waves by using
Api:RegisterWaveRequiredChoices(choice_id, min_wave, max_wave)
where min_wave
and max_wave
(inclusive) are the range of waves, can be set to nil to indicate no lower or upper limit
You can also custom the choice to only appear in certain waves by using
Api:RegisterWaveSpecialChoices(choice_id, min_wave, max_wave, limit_characters:table<string>)
where min_wave
and max_wave
(inclusive) are the range of waves (they can't be nil). If they are equal, the event will only appear in this wave, otherwise it will appear in the range of waves.
limit_characters
is a list of character ids that can trigger this event, if empty then all characters can trigger this event (not recommended as it will affect other mods)
If more than one RegisterWaveSpecialChoices
is registered in the same wave, the game will randomly select one of them.
Check out Advanced-Mod1
of our mod examples
Requirement config for choice event
requirement
can be set in the choice event options
There are the requirements that we support now:
options = {
...
{
...
-- limit relic
requirement = {
relic = "trolley_switch",
},
},
{
...
-- limit perk
requirement = {
perk = "gain_attack",
},
},
{
...
-- limit relic rarity
requirement = {
relic_rarity = "epic",
},
},
{
...
-- limit attributes, you can set multiple attributes, all of them must be satisfied to unlock
requirement = {
attributes = {
attack = 30,
},
},
},
{
...
-- limit selected options in previous events, all of them must be satisfied to unlock
requirement = {
choice_selected = {
["my_choice_id"] = { 1 },
},
},
},
}
You can ad more textures to change avatar during battle (Time Traveler's mood switch, PreBandleader's face for examples)
Open additions\textures
and put your extra textures there.
Let's add 2 new textures: my_character_angry.png
and my_character_cry.png
.
We also need to modify Actor json file to define some event:
Open additions\data\Actor.json
:
{
"actors": {
"COMBAT Unit First Mod Character" : {
"parent": "_COMBAT Unit Mob",
"unitActor": {
"resource": {
"name": "my_character_large"
},
"events": [
{
"actorTerm": {
"onCustomEvent": {
"identifier": "Transfer_Angry"
}
},
"actions": [
{
"setModel": {
"type": "Sprite",
"name": "my_character_angry"
}
}
]
},
{
"actorTerm": {
"onCustomEvent": {
"identifier": "Transfer_Cry"
}
},
"actions": [
{
"setModel": {
"type": "Sprite",
"name": "my_character_cry"
}
}
]
},
],
"modelScale": 0.5
}
}
}
}
Then you can call send actor event to change the sprite during battle!
-- event_name is what we have just definied in actor "Transfer_Angry" or "Transfer_Cry"
-- notice the unit is the actual unit, so use caster.unit
Api:SendActorEvent(unit, event_name)
Your mod now has everything. Let's check out how it plays:
first_mod.mp4
Pretty nice, isn't it?
If you encounter problems when following this guide, you can download our completed tutorial mod and use it as a reference.
The easiest way to share your mod with other players is to publish it to Steam Workshop. To do that, follow these steps:
- Open the mod manager UI by pressing "Ctrl + F8" in the game.
- Click on the "Upload to
" button.
- A popup will appear, allowing you to add change notes for your mod update. Fill in the necessary information.
- Click "Start upload" to begin the upload process.
- Once the upload is complete, a unique ID for your mod will be generated by Steam and saved in the
manifest.json
file. - Make sure to save the updated
manifest.json
file, as you will need the Steam ID to publish future updates for the same mod. - If the
manifest.json
file contains a Steam ID, the mod manager UI will display a "View on" button. Clicking this button will open the Steam page for your mod.
- As the mod author, you can add additional screenshots for your mod on the Steam page.
- On the Steam page, change the mod's visibilty from private to public.
Congratulations! Your mod is now published on the Steam Workshop and available for other players to enjoy.
We also provide a set of APIs to retrieve data used in the base game, allowing mode developers to reuse them. The following APIs will print data to the log, which is stored in the mods' root directory (Press Ctrl + F8, then click "Open local mods folder").
MOD_MANAGER:AddMod(function(Api)
Api:PrintCharacters()
Api:PrintAttributes()
Api:PrintMissiles()
Api:PrintPerks()
Api:PrintRelics()
Api:PrintChoices()
Api:PrintBosses()
end)
This tutorial only covers the modding basics to create a functional mod. To learn more about how to make Meme Mayhem mods, check out our mod examples here.
When createing your own mod, you may encounter more problems. Check out our FAQ page for the most commonly asked questions. If it still doesn't solve your problem, feel free to ask questions by opening issues.