Multi-tile mobs consist of several mobs occupying a pattern of tiles. One of the mobs is the "head" mob; this mob creates the "part" mobs, controls movement, handles events, and may have a client connected to it. The others are "part" mobs; when the head mob moves, they move with it, maintaining a constant delta x and delta y. The icons and CreateParts proc for the red smiley in Smileys of Unusual Size are given as an example. The icons (click the links to download them as .dmi files) are named as follows and placed in the directory mob/sous under the project's icons directory:
![]() | ![]() |
| red12.dmi | red22.dmi |
![]() | ![]() |
| red11.dmi | red21.dmi |
The following code defines the smiley to occupy four tiles in a 2x2 square, with the bottom left mob as the head:
/mob/multi/head/sous/red
CreateParts()
icon = 'mob/sous/red11.dmi'
density = 1
CreateParts()
CreatePart('mob/sous/red21.dmi',1,0,1)
CreatePart('mob/sous/red12.dmi',0,1,1)
CreatePart('mob/sous/red22.dmi',1,1,1)
After the head's New proc has called the CreateParts proc, the smiley will look
like this:



The code for the multi-tile mob library itself is as follows. Note that the changes to /turf/Enter are fully compatible with the changes to /turf/Enter made in the Dense Corner Cutting Prevention example, and they can both be included in the same proc.
/turf
Enter(O)
var/turf/t
var/b
// Only dense objects can be blocked.
if (O:density)
// A dense turf always blocks a dense object.
if (density)
return 0
// Dense objects are blocked by other dense objects at the
// destination unless both objects are part of the same multipart
// mob.
if (istype(O,/mob/multi))
for (b in contents)
if ((!(istype(b,/mob/multi)&&(b:head==O:head)))&&b:density)
return 0
else
for (b in contents)
if (b:density)
return 0
// The object can enter if it wasn't blocked.
return 1
// /mob/multi defines vars common to both "head" mobs and "part" mobs.
/mob/multi
var/mob/multi/head/head
var/delta_x
var/delta_y
// Part mobs are created by the CreateParts proc of a head mob.
/mob/multi/part
New(Loc,mob/multi/head/h,i,dx,dy,d)
// Create the part.
..(Loc)
// The following vars must be given as arguments to New.
head=h
icon=i
delta_x=dx
delta_y=dy
density=d
// The following vars must be copied from the head.
name=head.name
icon_state=head.icon_state
visibility=head.visibility
luminosity=head.luminosity
// Add this part to the head's parts list.
head.parts.Add(src)
Move(Loc,Dir)
// If the head has checked the move, perform it.
if (head.move_ok)
return ..(Loc,Dir)
// If the head hasn't checked the move, tell it to move to the location
// that would cause it to tell this part to move to the location given
// to this Move proc
else
if (isturf(Loc))
return head.Move(locate(Loc:x-delta_x,Loc:y-delta_y,Loc:z),Dir)
else
return head.Move(Loc,Dir)
// The following events must be handled by the head.
Click(Location)
return head.Click(Location)
DblClick(Location)
return head.DblClick(Location)
// Head mobs handle movement verification and mouse events
/mob/multi/head
var/list/parts
var/move_ok
New(Loc)
// Create the head.
..(Loc)
// Add it to the parts list.
parts=list(src)
// Set head pointer to refer to self.
head=src
// delta_x and delta_y must be zero for the head.
delta_x=0
delta_y=0
// Initialize move_ok flag.
move_ok=0
// Create parts.
CreateParts()
Del()
var/mob/multi/part/p
// Delete parts.
for (p in parts-src)
del p
// Delete head.
..()
Move(Loc,Dir)
var/mob/multi/o
var/turf/t
var/area/a
// Perform the move if it has already been checked.
if (move_ok)
return ..(Loc,Dir)
// Fail if any part cannot exit its current location.
for (o in parts)
if (o.loc)
if (!o.loc:Exit(o))
return 0
// If entering a turf, parts must move to turfs determined by their
// delta_x & delta_y.
if (isturf(Loc))
for (o in parts)
// Find the destination turf for this part.
t=locate(Loc:x+o.delta_x,Loc:y+o.delta_y,Loc:z)
// If this part must cross an area boundary, check that it can
// leave its current area and enter its destination area.
a=GetArea(o)
if (a!=t.loc)
if (!((isnull(a)||a.Exit(o))&&t.loc:Enter(o)))
return 0
// Can this part enter its destination turf?
if (!t.Enter(o))
return 0
// All parts can move to their destination turfs. Set the move_ok
// flag to signal that parts should move themselves instead of
// asking the head to verify the move.
move_ok=1
// Move the parts, including the head.
for (o in parts)
t=locate(Loc:x+o.delta_x,Loc:y+o.delta_y,Loc:z)
o.Move(t,Dir)
// If entering a non-turf, parts must move to null.
else
// Can the head enter its destination?
if (!Loc:Enter(src))
return 0
// Set the move_ok flag.
move_ok=1
// Perform the move.
Move(Loc,Dir)
// Move other parts to null.
for (o in parts-src)
if (o.loc)
o.Move(null,Dir)
// Clear the move_ok flag.
move_ok=0
// Indicate success to caller.
return 1
Login()
var/mob/multi/o
.=..()
// The name var has now been changed to match the key, so the name var
// must be updated for each part.
for (o in parts)
o.name=name
proc/CreateParts()
// Stub.
//
// The parts to be created will be defined in derived classes.
proc/CreatePart(i,dx,dy,d)
// If the head is in a turf, parts must be created in turfs determined
// by their delta_x and delta_y vars.
if (isturf(loc))
new /mob/multi/part(locate(loc:x+dx,loc:y+dy,loc:z),src,i,dx,dy,d)
// Otherwise they must be created in null.
else
new /mob/multi/part(null,src,i,dx,dy,d)