Initial commit: OpenRA game engine
Some checks failed
Continuous Integration / Linux (.NET 8.0) (push) Has been cancelled
Continuous Integration / Windows (.NET 8.0) (push) Has been cancelled

Fork from OpenRA/OpenRA with one-click launch script (start-ra.cmd)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
let5sne.win10
2026-01-10 21:46:54 +08:00
commit 9cf6ebb986
4065 changed files with 635973 additions and 0 deletions

Binary file not shown.

View File

@@ -0,0 +1,12 @@
dropdown-difficulty =
.label = Difficulty
.description = The difficulty of the mission
options-difficulty =
.easy = Easy
.normal = Normal
.hard = Hard
.tough = Real tough guy
## rules.yaml
briefing = GDI has imported a Nuclear Detonator in an attempt to sway a few local political leaders. Infiltrate the base and steal the detonator. A chopper will be sent to meet you at a designated landing zone. Look for the landing flare once you have stolen the device.

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -0,0 +1,602 @@
MapFormat: 12
RequiresMod: cnc
Title: 06b: Steal the Detonator
Author: Westwood Studios
Tileset: DESERT
MapSize: 64,64
Bounds: 15,12,47,50
Visibility: MissionSelector
Categories: Campaign
LockPreview: True
Players:
PlayerReference@GDI:
Name: GDI
Faction: gdi
Color: F5D378
Enemies: Nod
Bot: campaign
PlayerReference@Neutral:
Name: Neutral
OwnsWorld: True
NonCombatant: True
Faction: gdi
PlayerReference@Civilians:
Name: Civilians
NonCombatant: True
Faction: gdi
Enemies: Nod
Bot: campaign
PlayerReference@Nod:
Name: Nod
AllowBots: False
Playable: True
Required: True
LockFaction: True
Faction: nod
LockColor: True
Color: FE1100
LockSpawn: True
LockTeam: True
Enemies: GDI, Civilians
Actors:
Actor0: sbag
Location: 54,29
Owner: GDI
Actor1: sbag
Location: 50,29
Owner: GDI
Actor2: sbag
Location: 49,29
Owner: GDI
Actor3: sbag
Location: 48,29
Owner: GDI
Actor4: sbag
Location: 47,29
Owner: GDI
Actor5: sbag
Location: 46,29
Owner: GDI
Actor6: sbag
Location: 45,29
Owner: GDI
Actor7: sbag
Location: 44,29
Owner: GDI
Actor8: sbag
Location: 43,29
Owner: GDI
Actor9: sbag
Location: 42,29
Owner: GDI
Actor10: sbag
Location: 41,29
Owner: GDI
Actor11: sbag
Location: 40,29
Owner: GDI
Actor12: sbag
Location: 54,28
Owner: GDI
Actor13: cycl
Location: 48,28
Owner: GDI
Actor14: cycl
Location: 47,28
Owner: GDI
Actor15: cycl
Location: 46,28
Owner: GDI
Actor16: sbag
Location: 40,28
Owner: GDI
Actor17: sbag
Location: 54,27
Owner: GDI
Actor18: cycl
Location: 48,27
Owner: GDI
Actor19: cycl
Location: 46,27
Owner: GDI
Actor20: sbag
Location: 40,27
Owner: GDI
Actor21: sbag
Location: 54,26
Owner: GDI
Actor22: cycl
Location: 48,26
Owner: GDI
Actor23: cycl
Location: 47,26
Owner: GDI
Actor24: cycl
Location: 46,26
Owner: GDI
Actor25: sbag
Location: 40,26
Owner: GDI
Actor26: sbag
Location: 54,25
Owner: GDI
Actor27: sbag
Location: 40,25
Owner: GDI
Actor28: sbag
Location: 54,24
Owner: GDI
Actor29: sbag
Location: 54,23
Owner: GDI
Actor30: sbag
Location: 54,22
Owner: GDI
Actor31: sbag
Location: 54,21
Owner: GDI
Actor32: sbag
Location: 40,21
Owner: GDI
Actor33: sbag
Location: 54,20
Owner: GDI
Actor34: sbag
Location: 40,20
Owner: GDI
Actor35: sbag
Location: 54,19
Owner: GDI
Actor36: sbag
Location: 40,19
Owner: GDI
Actor37: sbag
Location: 54,18
Owner: GDI
Actor38: sbag
Location: 40,18
Owner: GDI
Actor39: sbag
Location: 54,17
Owner: GDI
Actor40: sbag
Location: 53,17
Owner: GDI
Actor41: sbag
Location: 52,17
Owner: GDI
Actor42: sbag
Location: 51,17
Owner: GDI
Actor43: sbag
Location: 50,17
Owner: GDI
Actor44: sbag
Location: 49,17
Owner: GDI
Actor45: sbag
Location: 48,17
Owner: GDI
Actor46: sbag
Location: 47,17
Owner: GDI
Actor47: sbag
Location: 46,17
Owner: GDI
Actor48: sbag
Location: 45,17
Owner: GDI
Actor49: sbag
Location: 44,17
Owner: GDI
Actor50: sbag
Location: 43,17
Owner: GDI
Actor51: sbag
Location: 42,17
Owner: GDI
Actor52: sbag
Location: 41,17
Owner: GDI
Actor53: sbag
Location: 40,17
Owner: GDI
Actor54: t18
Location: 28,31
Owner: Neutral
Actor55: t08
Location: 25,26
Owner: Neutral
Actor56: t08
Location: 27,21
Owner: Neutral
Actor57: t18
Location: 25,20
Owner: Neutral
Actor58: t18
Location: 33,45
Owner: Neutral
Actor59: t18
Location: 23,48
Owner: Neutral
Actor60: t08
Location: 52,38
Owner: Neutral
Actor61: t08
Location: 55,47
Owner: Neutral
Actor62: rock6
Location: 24,55
Owner: Neutral
Actor63: t08
Location: 32,58
Owner: Neutral
Actor64: t08
Location: 35,56
Owner: Neutral
Actor65: t08
Location: 30,50
Owner: Neutral
Actor66: t08
Location: 35,52
Owner: Neutral
Actor67: t08
Location: 37,56
Owner: Neutral
Actor68: t08
Location: 16,53
Owner: Neutral
Actor69: t08
Location: 25,61
Owner: Neutral
Actor70: t08
Location: 17,41
Owner: Neutral
Actor71: t08
Location: 49,34
Owner: Neutral
Actor72: t08
Location: 27,48
Owner: Neutral
Actor73: t08
Location: 34,36
Owner: Neutral
Actor74: t08
Location: 25,52
Owner: Neutral
Actor75: t08
Location: 43,30
Owner: Neutral
Actor76: rock1
Location: 45,29
Owner: Neutral
Actor77: t18
Location: 48,44
Owner: Neutral
Actor78: t18
Location: 58,45
Owner: Neutral
Actor79: t18
Location: 35,38
Owner: Neutral
Actor80: t18
Location: 50,55
Owner: Neutral
Actor81: t18
Location: 23,15
Owner: Neutral
Actor82: t18
Location: 15,13
Owner: Neutral
Actor83: t08
Location: 39,14
Owner: Neutral
Actor85: nuke
Location: 44,18
Owner: GDI
Obj2Actor1: v35
Location: 36,57
Owner: Civilians
Obj2Actor2: v34
Location: 34,56
Owner: Civilians
Obj2Actor3: v33
Location: 32,56
Owner: Civilians
Obj2Actor4: v30
Location: 36,51
Owner: Civilians
Obj2Actor5: v30
Location: 30,52
Owner: Civilians
Obj2Actor6: v29
Location: 36,52
Owner: Civilians
Obj2Actor7: v27
Location: 24,53
Owner: Civilians
Obj2Actor8: v27
Location: 25,53
Owner: Civilians
Obj2Actor9: v27
Location: 26,53
Owner: Civilians
Obj2Actor10: v26
Location: 31,53
Owner: Civilians
Obj2Actor0: v25
Location: 30,50
Owner: Civilians
Obj2Actor11: v24
Location: 29,52
Owner: Civilians
Actor98: v23
Location: 32,57
Owner: Civilians
Obj2Actor12: v22
Location: 35,53
Owner: Civilians
Obj2Actor13: v21
Location: 25,57
Owner: Civilians
Obj2Actor14: v20
Location: 26,54
Owner: Civilians
Actor104: silo
Location: 52,20
Owner: GDI
Actor106: gtwr
Location: 55,29
Owner: GDI
Actor107: gtwr
Location: 49,30
Owner: GDI
Actor108: hq
Location: 52,25
Owner: GDI
Actor109: proc
Location: 48,21
Owner: GDI
Actor110: pyle
Location: 46,18
Owner: GDI
Actor111: nuke
Location: 42,18
Owner: GDI
Actor113: mtnk
Location: 19,38
Owner: GDI
Actor114: jeep
Location: 19,52
Owner: GDI
Actor115: mtnk
Location: 36,37
Owner: GDI
Facing: 640
Actor116: jeep
Location: 29,24
Owner: GDI
Facing: 384
Actor117: mtnk
Location: 24,48
Owner: GDI
Facing: 768
Actor118: mtnk
Location: 28,42
Owner: GDI
Facing: 640
Actor128: e2
Location: 18,41
Owner: GDI
SubCell: 1
Actor129: e2
Location: 42,28
Owner: GDI
SubCell: 4
Actor130: e2
Location: 43,28
Owner: GDI
SubCell: 3
Actor131: e2
Location: 43,28
Owner: GDI
SubCell: 1
Actor132: e2
Location: 42,28
Owner: GDI
SubCell: 2
Actor133: e1
Location: 41,28
Owner: GDI
SubCell: 2
Actor134: e1
Location: 41,28
Owner: GDI
SubCell: 3
Actor135: e1
Location: 41,28
Owner: GDI
SubCell: 4
Actor136: e1
Location: 41,28
Owner: GDI
SubCell: 1
Actor137: e3
Location: 27,45
Owner: GDI
Facing: 640
SubCell: 4
Actor138: e1
Location: 25,58
Owner: GDI
Facing: 640
SubCell: 3
Actor139: c3
Location: 36,58
Owner: Civilians
SubCell: 0
Actor140: c5
Location: 37,58
Owner: Civilians
SubCell: 0
Actor141: e1
Location: 31,59
Owner: GDI
Facing: 768
SubCell: 4
Actor142: e2
Location: 31,57
Owner: GDI
SubCell: 4
Actor143: e2
Location: 37,54
Owner: GDI
SubCell: 0
Actor144: e2
Location: 34,60
Owner: GDI
Facing: 768
SubCell: 3
Actor157: e1
Location: 22,60
Owner: GDI
SubCell: 1
Actor158: e1
Location: 17,39
Owner: GDI
SubCell: 4
Actor160: e3
Location: 27,46
Owner: GDI
Facing: 128
SubCell: 1
Actor161: e3
Location: 33,45
Owner: GDI
Facing: 768
SubCell: 4
Actor162: e2
Location: 34,45
Owner: GDI
Facing: 896
SubCell: 0
waypoint27: waypoint
Location: 60,18
Owner: Neutral
waypoint26: waypoint
Location: 15,15
Owner: Neutral
waypoint25: waypoint
Location: 54,52
Owner: Neutral
waypoint17: waypoint
Location: 53,53
Owner: Neutral
waypoint10: waypoint
Location: 59,23
Owner: Neutral
waypoint9: waypoint
Location: 52,13
Owner: Neutral
waypoint8: waypoint
Location: 59,13
Owner: Neutral
waypoint7: waypoint
Location: 23,46
Owner: Neutral
waypoint6: waypoint
Location: 37,46
Owner: Neutral
waypoint5: waypoint
Location: 37,41
Owner: Neutral
waypoint4: waypoint
Location: 26,40
Owner: Neutral
waypoint3: waypoint
Location: 24,31
Owner: Neutral
waypoint2: waypoint
Location: 32,23
Owner: Neutral
waypoint1: waypoint
Location: 44,24
Owner: Neutral
waypoint0: waypoint
Location: 59,18
Owner: Neutral
UnitsEntryVehicle: waypoint
Location: 18,12
Owner: Neutral
UnitsRallyVehicle1: waypoint
Location: 18,21
Owner: Neutral
UnitsRallyVehicle2: waypoint
Location: 18,19
Owner: Neutral
UnitsEntryGunner: waypoint
Location: 16,12
Owner: Neutral
UnitsRallyGunner: waypoint
Location: 16,20
Owner: Neutral
UnitsEntryRocket: waypoint
Location: 22,12
Owner: Neutral
UnitsRallyRocket: waypoint
Location: 20,20
Owner: Neutral
ChnEntry: waypoint
Location: 26,61
Owner: Neutral
Detonator: CRATE.plain
Location: 47,27
Owner: GDI
Chn1Actor1: fact
Location: 48,18
Owner: GDI
Chn1Actor2: silo
Location: 51,18
Owner: GDI
Chn2Actor1: weap
Location: 51,22
Owner: GDI
Atk1Actor1: gtwr
Location: 39,25
Owner: GDI
Atk1Actor2: gtwr
Location: 39,21
Owner: GDI
Atk2Actor1: e2
Location: 18,36
Owner: GDI
SubCell: 3
Atk2Actor2: e2
Location: 17,36
Owner: GDI
SubCell: 1
waypoint13: waypoint
Owner: Neutral
Location: 43,58
Obj2UnitsEntry: waypoint
Owner: Neutral
Location: 45,61
Actor169: mtnk
Owner: GDI
Location: 33,54
Facing: 368
Rules: cnc|rules/campaign-maprules.yaml, cnc|rules/campaign-tooltips.yaml, cnc|rules/campaign-palettes.yaml, rules.yaml
FluentMessages: cnc|fluent/lua.ftl, cnc|fluent/campaign.ftl, map.ftl

View File

@@ -0,0 +1,175 @@
--[[
Copyright (c) The OpenRA Developers and Contributors
This file is part of OpenRA, which is free software. It is made
available to you under the terms of the GNU General Public License
as published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version. For more
information, see COPYING.
]]
NodUnitsVehicle1 =
{
tough = { 'bggy', 'bike', 'bike' },
hard = { 'bggy', 'bggy', 'bike', 'bike' },
normal = { 'bggy', 'bggy', 'bike', 'bike', 'bike' },
easy = { 'bggy', 'bggy', 'bggy', 'bike', 'bike', 'bike', 'bike' }
}
NodUnitsVehicle2 =
{
tough = { 'ltnk', 'ltnk' },
hard = { 'ltnk', 'ltnk', 'ltnk' },
normal = { 'ltnk', 'ltnk', 'ltnk', 'ltnk' },
easy = { 'ltnk', 'ltnk', 'ltnk', 'ltnk', 'ltnk' }
}
NodUnitsGunner =
{
tough = { 'e1', 'e1', 'e1', 'e1' },
hard = { 'e1', 'e1', 'e1', 'e1', 'e1' },
normal = { 'e1', 'e1', 'e1', 'e1', 'e1', 'e1', 'e1' },
easy = { 'e1', 'e1', 'e1', 'e1', 'e1', 'e1', 'e1', 'e1', 'e1', 'e1' }
}
NodUnitsRocket =
{
tough = { 'e3', 'e3', 'e3', 'e3' },
hard = { 'e3', 'e3', 'e3', 'e3', 'e3' },
normal = { 'e3', 'e3', 'e3', 'e3', 'e3', 'e3', 'e3' },
easy = { 'e3', 'e3', 'e3', 'e3', 'e3', 'e3', 'e3', 'e3', 'e3', 'e3' }
}
Gdi1Units = { 'e1', 'e1', 'e2', 'e2', 'e2' }
Obj2Units = { 'ftnk', 'e4', 'e4' }
HuntCellTriggerActivator = { CPos.New(61,34), CPos.New(60,34), CPos.New(59,34), CPos.New(58,34), CPos.New(57,34), CPos.New(56,34), CPos.New(55,34), CPos.New(61,33), CPos.New(60,33), CPos.New(59,33), CPos.New(58,33), CPos.New(57,33), CPos.New(56,33) }
DzneCellTriggerActivator = { CPos.New(50,30), CPos.New(49,30), CPos.New(48,30), CPos.New(47,30), CPos.New(46,30), CPos.New(45,30), CPos.New(50,29), CPos.New(49,29), CPos.New(48,29), CPos.New(47,29), CPos.New(46,29), CPos.New(45,29), CPos.New(50,28), CPos.New(49,28), CPos.New(48,28), CPos.New(47,28), CPos.New(46,28), CPos.New(45,28), CPos.New(50,27), CPos.New(49,27), CPos.New(46,27), CPos.New(45,27), CPos.New(50,26), CPos.New(49,26), CPos.New(48,26), CPos.New(47,26), CPos.New(46,26), CPos.New(45,26), CPos.New(50,25), CPos.New(49,25), CPos.New(48,25), CPos.New(47,25), CPos.New(46,25), CPos.New(45,25) }
Win1CellTriggerActivator = { CPos.New(47,27) }
Win2CellTriggerActivator = { CPos.New(57,57), CPos.New(56,57), CPos.New(55,57), CPos.New(57,56), CPos.New(56,56), CPos.New(55,56), CPos.New(57,55), CPos.New(56,55), CPos.New(55,55), CPos.New(57,54), CPos.New(56,54), CPos.New(55,54), CPos.New(57,53), CPos.New(56,53), CPos.New(55,53), CPos.New(57,52), CPos.New(56,52), CPos.New(55,52) }
ChnCellTriggerActivator = { CPos.New(61,52), CPos.New(60,52), CPos.New(59,52), CPos.New(58,52), CPos.New(61,51), CPos.New(60,51), CPos.New(59,51), CPos.New(58,51), CPos.New(61,50), CPos.New(60,50), CPos.New(59,50), CPos.New(58,50) }
Chn1ActorTriggerActivator = { Chn1Actor1, Chn1Actor2 }
Chn2ActorTriggerActivator = { Chn2Actor1 }
Atk1ActorTriggerActivator = { Atk1Actor1, Atk1Actor2 }
Atk2ActorTriggerActivator = { Atk2Actor1, Atk2Actor2 }
Obj2ActorTriggerActivator = { Obj2Actor0, Obj2Actor1, Obj2Actor2, Obj2Actor3, Obj2Actor4, Obj2Actor5, Obj2Actor6, Obj2Actor7, Obj2Actor8, Obj2Actor9, Obj2Actor10, Obj2Actor11, Obj2Actor12, Obj2Actor13, Obj2Actor14 }
Chn1Waypoints = { ChnEntry.Location, waypoint0.Location }
Chn2Waypoints = { ChnEntry.Location, waypoint0.Location }
Gdi5Waypoint = { waypoint1, waypoint2, waypoint3, waypoint4, waypoint5, waypoint6, waypoint7 }
OnAnyDamaged = function(actors, func)
local triggered
Utils.Do(actors, function(actor)
Trigger.OnDamaged(actor, function()
if triggered then
return
end
triggered = true
func()
end)
end)
end
InsertNodUnits = function()
NodUnitsVehicle1 = NodUnitsVehicle1[Difficulty]
NodUnitsVehicle2 = NodUnitsVehicle2[Difficulty]
NodUnitsGunner = NodUnitsGunner[Difficulty]
NodUnitsRocket = NodUnitsRocket[Difficulty]
Media.PlaySpeechNotification(Nod, "Reinforce")
Camera.Position = UnitsRallyVehicle2.CenterPosition
Reinforcements.Reinforce(Nod, NodUnitsVehicle1, { UnitsEntryVehicle.Location, UnitsRallyVehicle1.Location }, 10)
Reinforcements.Reinforce(Nod, NodUnitsVehicle2, { UnitsEntryVehicle.Location, UnitsRallyVehicle2.Location }, 15)
Reinforcements.Reinforce(Nod, NodUnitsGunner, { UnitsEntryGunner.Location, UnitsRallyGunner.Location }, 15)
Reinforcements.Reinforce(Nod, NodUnitsRocket, { UnitsEntryRocket.Location, UnitsRallyRocket.Location }, 25)
end
WorldLoaded = function()
Nod = Player.GetPlayer("Nod")
GDI = Player.GetPlayer("GDI")
InitObjectives(Nod)
StealDetonator = AddPrimaryObjective(Nod, "steal-nuclear-detonator")
DestroyVillage = AddSecondaryObjective(Nod, "destroy-gdi-supporter-houses")
InsertNodUnits()
Trigger.OnEnteredFootprint(HuntCellTriggerActivator, function(a, id)
if a.Owner == Nod then
Utils.Do(GDI.GetGroundAttackers(), IdleHunt)
Trigger.RemoveFootprintTrigger(id)
end
end)
Trigger.OnEnteredFootprint(DzneCellTriggerActivator, function(a, id)
if a.Owner == Nod then
Actor.Create('flare', true, { Owner = Nod, Location = waypoint17.Location })
Trigger.RemoveFootprintTrigger(id)
end
end)
Trigger.OnAllRemovedFromWorld(Obj2ActorTriggerActivator, function()
Nod.MarkCompletedObjective(DestroyVillage)
Media.PlaySpeechNotification(Nod, "Reinforce")
Reinforcements.Reinforce(Nod, Obj2Units, { Obj2UnitsEntry.Location, waypoint13.Location }, 15)
end)
Trigger.OnEnteredFootprint(Win1CellTriggerActivator, function(a, id)
if a.Owner == Nod then
EvacuateObjective = AddPrimaryObjective(Nod, "move-to-evacuation-point")
Nod.MarkCompletedObjective(StealDetonator)
Trigger.RemoveFootprintTrigger(id)
end
end)
Trigger.OnEnteredFootprint(Win2CellTriggerActivator, function(a, id)
if a.Owner == Nod and EvacuateObjective then
Nod.MarkCompletedObjective(EvacuateObjective)
Trigger.RemoveFootprintTrigger(id)
end
end)
OnAnyDamaged(Chn1ActorTriggerActivator, function()
local cargo = Reinforcements.ReinforceWithTransport(GDI, 'tran', Gdi1Units, Chn1Waypoints, { ChnEntry.Location })[2]
Utils.Do(cargo, IdleHunt)
end)
OnAnyDamaged(Atk1ActorTriggerActivator, function()
for type, count in pairs({ ['e2'] = 2, ['jeep'] = 1, ['e1'] = 2}) do
Utils.Do(Utils.Take(count, GDI.GetActorsByType(type)), IdleHunt)
end
end)
OnAnyDamaged(Atk2ActorTriggerActivator, function()
for type, count in pairs({ ['e2'] = 2, ['e1'] = 2}) do
MoveAndHunt(Utils.Take(count, GDI.GetActorsByType(type)), Gdi5Waypoint)
end
end)
OnAnyDamaged(Chn2ActorTriggerActivator, function()
local cargo = Reinforcements.ReinforceWithTransport(GDI, 'tran', Gdi1Units, Chn2Waypoints, { ChnEntry.Location })[2]
Utils.Do(cargo, IdleHunt)
end)
Trigger.OnEnteredFootprint(ChnCellTriggerActivator, function(a, id)
if a.Owner == Nod then
Media.PlaySpeechNotification(Nod, "Reinforce")
Reinforcements.ReinforceWithTransport(Nod, 'tran', nil, { ChnEntry.Location, waypoint17.Location }, nil, nil, nil)
Trigger.RemoveFootprintTrigger(id)
end
end)
end
Tick = function()
if DateTime.GameTime > 2 and Nod.HasNoRequiredUnits() then
Nod.MarkFailedObjective(StealDetonator)
if EvacuateObjective then
Nod.MarkFailedObjective(EvacuateObjective)
end
end
end

View File

@@ -0,0 +1,41 @@
World:
LuaScript:
Scripts: campaign.lua, utils.lua, nod06b.lua
MusicPlaylist:
StartingMusic: rout
VictoryMusic: nod_win1
MissionData:
Briefing: briefing
BriefingVideo: nod6.vqa
StartVideo: sundial.vqa
LossVideo: banner.vqa
ScriptLobbyDropdown@difficulty:
ID: difficulty
Label: dropdown-difficulty.label
Description: dropdown-difficulty.description
Values:
easy: options-difficulty.easy
normal: options-difficulty.normal
hard: options-difficulty.hard
tough: options-difficulty.tough
Default: normal
Player:
PlayerResources:
DefaultCash: 0
^Palettes:
IndexedPlayerPalette:
PlayerIndex:
Civilians: 192, 164, 132, 155, 133, 197, 112, 12, 163, 132, 155, 133, 134, 197, 154, 198
IndexedPlayerPalette@units:
PlayerIndex:
Civilians: 192, 164, 132, 155, 133, 197, 112, 12, 163, 132, 155, 133, 134, 197, 154, 198
FLARE:
Tooltip:
ShowOwnerRow: false
TRAN:
-Selectable:
Interactable: