File size: 10,200 Bytes
b6a38d7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
if not Platform.editor then return end

GameMarkerStats = {
	AL_Carry = 72,
	AL_Cower = 1,
	AL_Defender_PlayAnimVariation = 52,
	AL_Football = 8,
	AL_MineWorkPick = 7,
	AL_MineWorkShovel = 10,
	AL_MineWorkSift = 10,
	AL_PlayAnimVariation = 1256,
	AL_Prostitute_Idle = 13,
	AL_Prostitute_Parade = 11,
	AL_Roam = 1278,
	AL_SitChair = 313,
	AL_Talk = 25,
	AL_WallLean = 124,
	AL_WallLean_NoSnap = 6,
	AL_WallLean_Prostitute = 11,
	AL_WeaponAim = 3,
	AmbientLifeMarker = 10,
	CameraCollider = 21016,
--	DestroyedSlabMarker = 6202, -- placed by code, disable placing through editor
	FloatingDummy = 1438,
	ForcedImpassableMarker = 9,
	["GridMarker-BorderArea"] = 171,
	["GridMarker-Defender"] = 233,
	["GridMarker-DefenderPriority"] = 903,
	["GridMarker-Entrance"] = 216,
	["GridMarker-Logic"] = 64,
	["GridMarker-Position"] = 213,
	["GridMarker-AIBiasMarker"] = 16,
	["GridMarker-AmbientLifeRepulsor"] = 18,
	["GridMarker-AmbientZoneMarker"] = 163,
	["GridMarker-AmbientZone_Animal"] = 34,
	["GridMarker-BombardMarker"] = 6,
	["GridMarker-ContainerIntelMarker"] = 66,
	["GridMarker-ContainerMarker"] = 604,
	["GridMarker-CustomInteractable"] = 315,
	["GridMarker-DeploymentMarker"] = 11,
	["GridMarker-ExamineMarker"] = 156,
	["GridMarker-ExitZoneInteractable"] = 584,
	["GridMarker-GrenadeThrowMarker"] = 4,
	["GridMarker-HackMarker"] = 167,
	["GridMarker-HerbMarker"] = 357,
	["GridMarker-IntelMarker"] = 459,
	["GridMarker-LightsMarker"] = 1,
	["GridMarker-OverheardMarker"] = 152,
	["GridMarker-PostWorldFlipDefenderMarker"] = 1,
	["GridMarker-RepositionMarker"] = 5,
	["GridMarker-SalvageMarker"] = 265,
	["GridMarker-TrapSpawnMarker"] = 303,
	["GridMarker-UnitMarker"] = 1200,
	["GridMarker-WaypointMarker"] = 1403,
	MachineGunEmplacement = 28,
	NoteMarker = 103,
	TravelMarker_01 = 47,
	TravelMarker_02 = 58,
	TravelMarker_03 = 64,
	TravelMarker_04 = 74,
	TravelMarker_05 = 37,
	TravelMarker_06 = 100,
	TravelMarker_07 = 59,
	TravelMarker_08 = 100,
	WindMarker = 804,
	WindlessMarker = 38,
}

GameMarkerDocs = {
	["GridMarker-BorderArea"] = "This marker should be placed once on every map.\n\nIts area defines the playable zone of the map; its borders are visualized at runtime even in game mode, when the cursor approaches them.",
	["GridMarker-Defender"] = "Whenever two squads of two opposing sides find themselves on the same sector, the side currently holding the sector is 'defending' it, and its units are placed on the DefenderPriority (filled first) and Defender markers.",
	["GridMarker-Entrance"] = "Defines an area where enemies enter the map, and where ambient life and NPCs can leave the map.",
	["GridMarker-Logic"] = "A generic marker which does nothing by itself, and is used to events to conditions. Some of these conditions and events might treat the area of the marker itself (e.g. is a merc nearby).",
	["GridMarker-Position"] = "The default marker type, with no particular usage implied or implemented.\n\nIf you want to use markers indicating position in your mods in conjunction with some custom code, use this type.",
	["GridMarker-AIBiasMarker"] = "Used to tweak AI evaluations to make AI units prefer or avoid certain areas.",
	["GridMarker-AmbientLifeRepulsor"] = "Defines an area where ambient life will attempt not to go.",
	["GridMarker-AmbientZoneMarker"] = "Defines rules for spawning and behavior of random, non-specific NPCs (aka 'ambient life') in its area.",
	["GridMarker-AmbientZone_Animal"] = "A subclass of AmbientZoneMarker specific for spawning ambient animals.",
	["GridMarker-BombardMarker"] = "Defines an area where bombardment with heavy ordnanced can be executed using a BombardEffect.",
	["GridMarker-ContainerMarker"] = "Defines objects that function as a container, from which items can be taken.\n\nWhat items are provided is controlled via conditions and loot tables. The ContainerIntelMarker variation only works if corresponding intel has been revealed.",
	["GridMarker-CustomInteractable"] = "Defines objects that, when interacted with, check a list of conditions and execute a number of effects. Custom texts and interaction icons can also be provided.",
	["GridMarker-DeploymentMarker"] = "Defines an area where the player can deploy their mercs.",
	["GridMarker-ExamineMarker"] = "Defines objects that, when interacted with, check a list of conditions and execute a number of effects. The interaction text is always 'Examine'.",
	["GridMarker-ExitZoneInteractable"] = "Defines objects that, when interacted with, will lead the mercs to exit the map - usually small visual objects at the map's edges like milestones, road signs, trapdoors (for accessing underground sectors) etc.",
	["GridMarker-GrenadeThrowMarker"] = "Defines a location where a grenade hit can be simulated using a scripting effect.",
	["GridMarker-HackMarker"] = "Defines objects that, when interacted with, after a skill check for a specific skill, grant specific items.\n\nThe objects must be in the same collection as the marker.",
	["GridMarker-IntelMarker"] = "Defines an area which can display custom text on the ground when an intel-gathering activity succeeds.",
	["GridMarker-LightsMarker"] = "Defines an area where the lights can be turned on and off via a scripting effect, LightsSetState.",
	["GridMarker-OverheardMarker"] = "Defines a position which, when approached by mercs, will start a banter (an in-world mini-conversation) between other units in the vicinity.",
	["GridMarker-RepositionMarker"] = "Defines a position that should be occupied during Reposition phase if possible. A Repositioning unit that can reach this position will claim it and move there.",
	["GridMarker-TrapSpawnMarker"] = "Defines objects that will be spawned under specific conditions to act as a trap - e.g. explode, can be defused etc.",
	["GridMarker-UnitMarker"] = "Used to spawn specific units, with configurable appearance, interaction and behavior. Most campaign NPCs and some enemies are spawned this way.",
	["GridMarker-WaypointMarker"] = "Markers of this type can be used to define patrol routes, which can then be used by patrolling enemies or ambient life.",
}

GameMarkerDocs["GridMarker-DefenderPriority"] = GameMarkerDocs["GridMarker-Defender"]
GameMarkerDocs["GridMarker-PostWorldFlipDefenderMarker"] = GameMarkerDocs["GridMarker-Defender"]
GameMarkerDocs["GridMarker-ContainerIntelMarker"] = GameMarkerDocs["GridMarker-ContainerMarker"]
GameMarkerDocs["GridMarker-HerbMarker"] = GameMarkerDocs["GridMarker-HackMarker"]
GameMarkerDocs["GridMarker-SalvageMarker"] = GameMarkerDocs["GridMarker-HackMarker"]

local function compile_markers_place_data(stats)
	local data = {}
	for id, uses in pairs(stats) do
		table.insert(data, {
			id = id,
			use_count = uses,
			usage_segment =
				uses > 300 and 3 or
				uses > 200 and 2 or
				uses > 100 and 1 or 0,
			documentation = GameMarkerDocs[id],
		})
	end
	return data
end

GameMarkerPlaceData = compile_markers_place_data(GameMarkerStats)

-- don't include any markers not placed on even a single original JA3 map
if config.ModdingToolsInUserMode then
	local old_available_in_editor = available_in_editor
	function available_in_editor(entity, class_name)
		local data = EntityData[entity] or empty_table
		if data.editor_category == "Markers" and not GameMarkerStats[class_name] then
			return false
		end
		return old_available_in_editor(entity, class_name)
	end
end

local old_XEditorEnumPlaceableObjects = XEditorEnumPlaceableObjects
function XEditorEnumPlaceableObjects(callback)
	callback("SlabWallDoor", "SlabWallDoor", "Common", "Slab", "Door")
	callback("StairSlab", "StairSlab", "Common", "Slab", "Stairs")
	callback("FloorSlab", "FloorSlab", "Common", "Slab", "Floor")
	callback("WallSlab", "WallSlab", "Common", "Slab", "Wall")
	
	local list = {}
	Msg("GatherPlaceCategories", list)
	for _, entry in ipairs(list) do
		callback(table.unpack(entry))
	end
	
	if config.ModdingToolsInUserMode then
		local suffixes = {
			"<right>•",
			"<right>••",
			"<right>•••",
		}
		for _, item in ipairs(GameMarkerPlaceData) do
			local id = item.id
			local docs
			if item.documentation or item.use_count then
				local texts = {}
				local title = id:starts_with("GridMarker-") and id:sub(12) or id
				table.insert(texts, string.format("<style GedTitle><center>%s</style>", title))
				table.insert(texts, item.documentation)
				table.insert(texts, item.use_count and string.format("<style GedSmall>Used %d times across all maps.", item.use_count))
				item.documentation = table.concat(texts, "\n\n")
			end
			callback(id, id, "Common", "Markers", nil, suffixes[item.usage_segment], nil, nil, item)
		end
	else
		for _, class in ipairs(ClassDescendantsList("GridMarker")) do
			callback(class, class, "Common", "Markers")
		end
		ForEachPreset("GridMarkerType", function(preset)
			local id = "GridMarker-" .. preset.id
			callback(id, id, "Common", "Markers")
		end)
	end
	
	callback("DummyUnit", "DummyUnit", "Common", "Markers")
	callback("CMTPlane", "CMTPlane", "Common", "Markers")
	if not config.ModdingToolsInUserMode then
		callback("ActionCameraTestDummy_Player", "ActionCameraTestDummy_Player", "Common", "Markers")
		callback("ActionCameraTestDummy_Enemy", "ActionCameraTestDummy_Enemy", "Common", "Markers")
	end
	
	old_XEditorEnumPlaceableObjects(callback)
end

local old_XEditorPlaceObject = XEditorPlaceObject
function XEditorPlaceObject(id, is_cursor_object)
	if id:starts_with("GridMarker-") then
		local marker_type = id:sub(12)
		if IsKindOf(g_Classes[marker_type], "GridMarker") then
			return g_Classes[marker_type]:new()
		end
		local marker = GridMarker:new()
		marker:SetType(marker_type)
		return marker
	end
	return old_XEditorPlaceObject(id, is_cursor_object)
end

local old_EditorCanSelect = editor.CanSelect
function editor.CanSelect(obj)
	return not IsKindOfClasses(obj, "DebugCoverDraw", "DebugPassDraw", "PFTunnel", "RoofFXController") and obj.class ~= "LightCCD" and
		old_EditorCanSelect(obj)
end

local old_XEditorPlaceId = XEditorPlaceId
function XEditorPlaceId(obj)
	if obj.class == "GridMarker" then
		return obj.Type and Presets.GridMarkerType.Default[obj.Type] and ("GridMarker-" .. obj.Type) or obj.class
	else
		return old_XEditorPlaceId(obj)
	end
end