Skip to content

Custom gametypes

Lactozilla requested to merge gametype-clownery into next

This is a work-in-progress framework I wrote for custom gametypes. In a gross simplification, every piece of gamelogic belonging to certain gametypes were split off into their own flags that can be combined together to form a gametype. Those are called "gametype rules".

Rule list

These are the current gametype rules available:

Keyword Explanation or default availability
GTR_CAMPAIGN Linear Co-op map progression, don't allow random maps
GTR_RINGSLINGER Outside of Co-op, Competition, and Race (overriden by cv_ringslinger)
GTR_SPECTATORS Outside of Co-op, Competition, and Race
GTR_FRIENDLYFIRE Always allow friendly fire
GTR_LIVES Co-op and Competition
GTR_TEAMS Team Match, CTF
GTR_RACE Race and Competition
GTR_TAG Tag and Hide and Seek
GTR_POINTLIMIT Ringslinger point limit
GTR_TIMELIMIT Ringslinger time limit
GTR_HIDETIME Hide time (Tag and Hide and Seek)
GTR_HIDEFROZEN Frozen after hide time (Hide and Seek, but not Tag)
GTR_BLINDFOLDED Blindfolded view (Tag and Hide and Seek)
GTR_FIRSTPERSON First person camera
GTR_MATCHEMERALDS Ringslinger emeralds (Match and CTF)
GTR_TEAMFLAGS Gametype has team flags (CTF)
GTR_PITYSHIELD Award pity shield
GTR_DEATHPENALTY Death score penalty
GTR_NOSPECTATORSPAWN Use with GTR_SPECTATORS, spawn in the map instead of with the spectators
GTR_DEATHMATCHSTARTS Use deathmatch starts
GTR_SPECIALSTAGES Allow special stages
GTR_EMERALDTOKENS Spawn emerald tokens
GTR_EMERALDHUNT Emerald Hunt
GTR_SPAWNENEMIES Spawn enemies
GTR_ALLOWEXIT Allow exit sectors
GTR_NOTITLECARD Don't show the title card
GTR_OVERTIME Allow overtime
GTR_HURTMESSAGES Hit and death messages
GTR_SPAWNINVUL Babysitting deterrent

Default used ruleset, for reference:

// Gametype rules
UINT32 gametypedefaultrules[NUMGAMETYPES] =
{
	// Co-op
	GTR_CAMPAIGN|GTR_LIVES|GTR_SPAWNENEMIES|GTR_ALLOWEXIT|GTR_EMERALDHUNT|GTR_EMERALDTOKENS|GTR_SPECIALSTAGES,
	// Competition
	GTR_RACE|GTR_LIVES|GTR_SPAWNENEMIES|GTR_EMERALDTOKENS|GTR_SPAWNINVUL|GTR_ALLOWEXIT,
	// Race
	GTR_RACE|GTR_SPAWNENEMIES|GTR_SPAWNINVUL|GTR_ALLOWEXIT,

	// Match
	GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_MATCHEMERALDS|GTR_SPAWNINVUL|GTR_PITYSHIELD|GTR_DEATHPENALTY,
	// Team Match
	GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_TEAMS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_SPAWNINVUL|GTR_PITYSHIELD,

	// Tag
	GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_TAG|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_HIDETIME|GTR_BLINDFOLDED|GTR_SPAWNINVUL,
	// Hide and Seek
	GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_TAG|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_HIDETIME|GTR_BLINDFOLDED|GTR_SPAWNINVUL,

	// CTF
	GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_TEAMS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_MATCHEMERALDS|GTR_TEAMFLAGS|GTR_SPAWNINVUL|GTR_PITYSHIELD,
};

Creating new gametypes

Gametypes can be defined with either SOC or Lua. Here's an example of a gametype definition in SOC:

FreeSlot
TOL_HANGOUT

GameType Hangout
TypeOfLevel = Coop,Hangout
Spectators = True
NoSpectatorSpawn = True
NoTitleCard = True
AllowExit = False
SpawnEnemies = False
Lives = False
EmeraldHunt = False
EmeraldTokens = True
IntermissionType = int_coop
HeaderLeftColor = 103
HeaderRightColor = 109
Description = "Bruh" is a popular variant of the slang term "bro" that is often used as an interjection to convey frustration or disappointment at something. Online, the term is
#

Here, this gametype defines its own TypeOfLevel to be used in map headers, and toggles gametype rules using keywords. The level platter is also configured here. The description mush be terminated with a # character (number sign.)

srb20247

SOC keywords

Keyword Explanation Example
RULES Game type rules Rules = GTR_RINGSLINGER
IDENTIFIER Game type constant (GT_) Identifier = CHILLAX (turns into GT_CHILLAX)
DEFAULTTIMELIMIT Time limit in minutes DefaultTimeLimit = 10
DEFAULTPOINTLIMIT Point limit DefaultPointLimit = 1050
HEADERCOLOR Level platter header color HeaderColor = 15
HEADERLEFTCOLOR Level platter header color (left side) HeaderLeftColor = 103
HEADERRIGHTCOLOR Level platter header color (right side) HeaderRightColor = 109
DESCRIPTION Level platter description
RANKINGTYPE Score rankings type RankingType = GT_RACE
INTERMISSIONTYPE Intermission type IntermissionType = int_coop
TYPEOFLEVEL Type of level TypeOfLevel = Match,Tag

Intermission types

Constant Type
int_none Nothing
int_coop Single Player/Cooperative
int_match Match
int_teammatch Team Match
int_ctf CTF
int_spec Special Stage
int_race Race
int_comp Competition

In Lua, this would be the same definition:

freeslot("TOL_HANGOUT")

G_AddGametype({
	name = "Hangout",
	identifier = "chillax",
	typeoflevel = TOL_COOP|TOL_HANGOUT,
	rules = GTR_SPECTATORS|GTR_NOSPECTATORSPAWN|GTR_NOTITLECARD|GTR_EMERALDTOKENS,
	intermissiontype = int_coop,
	headerleftcolor = 103,
	headerrightcolor = 109,
	description = '"Bruh" is a popular variant of the slang term "bro" that is often used as an interjection to convey frustration or disappointment at something. Online, the term is'
})

Lua keywords

Keyword Explanation
name Game type name
identifier Game type constant (GT_)
rules Game type rules
typeoflevel Type of level
rankingtype Score rankings type
intermissiontype Intermission type
defaultpointlimit Point limit
defaulttimelimit Time limit in minutes
description Level platter description
headerleftcolor Level platter header color (left side)
headerrightcolor Level platter header color (right side)
headercolor Level platter header color

Lua hooks

Two new Lua hooks are available - TeamSwitch and ViewpointSwitch.

TeamSwitch

Hook format: addHook("TeamSwitch", functionname)
Function format: function(player_t player, int team, boolean fromspectators, boolean autobalance, boolean scramble)
Function return value: boolean (true to allow team switch, false to disallow team switch), nil (does nothing)

This hook runs when the player switches teams. Returning true allows team switch, and returning false disallows team switch.

ViewpointSwitch

Hook format: addHook("ViewpointSwitch", functionname)
Function format: function(player_t player, int newdisplayplayer, boolean forcedviewpointswitch)
Function return value: boolean (true to force viewpoint switch, false to skip player about to be viewed), nil (does nothing)

This hook runs when the player uses spy mode. Returning true forces a viewpoint switch, and returning false avoid viewing the other player. forcedviewpointswitch will be true if the viewpoint switch was not caused by F12 (example, the player currently being viewed leaves the netgame.)

Edited by Lactozilla

Merge request reports