diff --git a/sources/Gameplay/BaseClasses/Frontend/EPawn.uc b/sources/Gameplay/BaseClasses/Frontend/EPawn.uc
new file mode 100644
index 0000000..cfed609
--- /dev/null
+++ b/sources/Gameplay/BaseClasses/Frontend/EPawn.uc
@@ -0,0 +1,57 @@
+/**
+ * Interface for a *Pawn* - base class for any entity that can be
+ * controlled by player or AI. To avoid purity for the sake of itself, in
+ * Acedia it will also be bundled with such typical components as health,
+ * collision, etc.
+ * Copyright 2022 Anton Tarasenko
+ *------------------------------------------------------------------------------
+ * This file is part of Acedia.
+ *
+ * Acedia is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Acedia is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Acedia. If not, see .
+ */
+class EPawn extends EPlaceable
+ abstract;
+
+/**
+ * Returns current amount of health caller `EPawn`'s referred entity has,
+ * assuming that entity has a health component.
+ *
+ * @return Current amount of health caller `EPawn`'s entity has.
+ * If entity that caller `EPawn` refers to doesn't have health component -
+ * returns `0`.
+ */
+public function int GetHealth()
+{
+ return 0;
+}
+
+/**
+ * Returns current maximum amount of health caller `EPawn`'s referred entity can
+ * have, assuming that entity has a health component.
+ *
+ * @return Current maximum amount of health caller `EPawn`'s entity can have.
+ * If entity that caller `EPawn` refers to doesn't have health component -
+ * returns `0`.
+ */
+public function int GetMaxHealth();
+
+/**
+ * Produces a suicide event for caller `EPawn`, making it drain its health and
+ * change its state into dead, whatever it means for the caller `EPawn`.
+ */
+public function Suicide();
+
+defaultproperties
+{
+}
\ No newline at end of file
diff --git a/sources/Gameplay/BaseClasses/Frontend/EPlaceable.uc b/sources/Gameplay/BaseClasses/Frontend/EPlaceable.uc
new file mode 100644
index 0000000..3048041
--- /dev/null
+++ b/sources/Gameplay/BaseClasses/Frontend/EPlaceable.uc
@@ -0,0 +1,31 @@
+/**
+ * Interface for any entity that can be placed into the game world.
+ * Copyright 2022 Anton Tarasenko
+ *------------------------------------------------------------------------------
+ * This file is part of Acedia.
+ *
+ * Acedia is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Acedia is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Acedia. If not, see .
+ */
+class EPlaceable extends EInterface;
+
+/**
+ * Returns position of the caller `EPlaceable`
+ *
+ * @return Vector that describes position of the caller `EPlaceable`.
+ */
+public function Vector GetLocation();
+
+defaultproperties
+{
+}
\ No newline at end of file
diff --git a/sources/Gameplay/KF1Frontend/BaseImplementation/EKFPawn.uc b/sources/Gameplay/KF1Frontend/BaseImplementation/EKFPawn.uc
new file mode 100644
index 0000000..08f5f6c
--- /dev/null
+++ b/sources/Gameplay/KF1Frontend/BaseImplementation/EKFPawn.uc
@@ -0,0 +1,154 @@
+/**
+ * Implementation of `EPawn` for classic Killing Floor weapons that changes
+ * as little as possible and only on request from another mod, otherwise not
+ * altering gameplay at all.
+ * Copyright 2022 Anton Tarasenko
+ *------------------------------------------------------------------------------
+ * This file is part of Acedia.
+ *
+ * Acedia is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Acedia is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Acedia. If not, see .
+ */
+class EKFPawn extends EPawn;
+
+var private NativeActorRef pawnReference;
+
+protected function Finalizer()
+{
+ _.memory.Free(pawnReference);
+ pawnReference = none;
+}
+
+/**
+ * Creates new `EKFPawn` that refers to the `pawnInstance` weapon.
+ *
+ * @param pawnInstance Native pawn class that new `EKFPawn` will represent.
+ * @return New `EKFPawn` that represents given `pawnInstance`.
+ */
+public final static /*unreal*/ function EKFPawn Wrap(Pawn pawnInstance)
+{
+ local EKFPawn newReference;
+
+ if (pawnInstance == none) {
+ return none;
+ }
+ newReference = EKFPawn(__().memory.Allocate(class'EKFPawn'));
+ newReference.pawnReference = __().unreal.ActorRef(pawnInstance);
+ return newReference;
+}
+
+public function EInterface Copy()
+{
+ local Pawn pawnInstance;
+
+ pawnInstance = GetNativeInstance();
+ return Wrap(pawnInstance);
+}
+
+public function bool Supports(class newInterfaceClass)
+{
+ if (newInterfaceClass == none) return false;
+ if (newInterfaceClass == class'EPlaceable') return true;
+ if (newInterfaceClass == class'EKFPawn') return true;
+
+ return false;
+}
+
+public function EInterface As(class newInterfaceClass)
+{
+ if (!IsExistent()) {
+ return none;
+ }
+ if ( newInterfaceClass == class'EPlaceable'
+ || newInterfaceClass == class'EKFPawn')
+ {
+ return Copy();
+ }
+ return none;
+}
+
+public function bool IsExistent()
+{
+ return (GetNativeInstance() != none);
+}
+
+public function bool SameAs(EInterface other)
+{
+ local EKFPawn otherPawn;
+
+ otherPawn = EKFPawn(other);
+ if (otherPawn == none) {
+ return false;
+ }
+ return (GetNativeInstance() == otherPawn.GetNativeInstance());
+}
+
+/**
+ * Returns `Pawn` instance represented by the caller `EKFPawn`.
+ *
+ * @return `Pawn` instance represented by the caller `EKFPawn`.
+ */
+public final /*unreal*/ function Pawn GetNativeInstance()
+{
+ if (pawnReference != none) {
+ return Pawn(pawnReference.Get());
+ }
+ return none;
+}
+
+public function Vector GetLocation()
+{
+ local Pawn pawnInstance;
+
+ pawnInstance = GetNativeInstance();
+ if (pawnInstance != none) {
+ return pawnInstance.location;
+ }
+ return Vect(0.0, 0.0, 0.0);
+}
+
+public function int GetHealth()
+{
+ local Pawn pawnInstance;
+
+ pawnInstance = GetNativeInstance();
+ if (pawnInstance != none) {
+ return pawnInstance.health;
+ }
+ return 0;
+}
+
+public function int GetMaxHealth()
+{
+ local Pawn pawnInstance;
+
+ pawnInstance = GetNativeInstance();
+ if (pawnInstance != none) {
+ return int(pawnInstance.healthMax);
+ }
+ return 0;
+}
+
+public function Suicide()
+{
+ local Pawn pawnInstance;
+
+ pawnInstance = GetNativeInstance();
+ if (pawnInstance != none) {
+ pawnInstance.Suicide();
+ }
+}
+
+defaultproperties
+{
+}
\ No newline at end of file
diff --git a/sources/Players/EPlayer.uc b/sources/Players/EPlayer.uc
index 29c895d..5aee122 100644
--- a/sources/Players/EPlayer.uc
+++ b/sources/Players/EPlayer.uc
@@ -180,6 +180,27 @@ public final function Vector GetLocation()
return Vect(0.0, 0.0, 0.0);
}
+/**
+ * If caller `EPlayer` currently owns a pawn, then this method will return
+ * `EPawn` interface to it.
+ *
+ * @return `EPawn` interface to pawn of the caller `EPlayer`.
+ * `none` if caller `EPayer` is non-existent or does not have a pawn.
+ */
+public final function EPawn GetPawn()
+{
+ local Pawn myPawn;
+ local PlayerController myController;
+
+ if (controller == none) return none;
+ myController = PlayerController(controller.Get());
+ if (myController == none) return none;
+ myPawn = myController.pawn;
+ if (myPawn == none) return none;
+
+ return class'EKFPawn'.static.Wrap(myPawn);
+}
+
// `PlayerReplicationInfo` associated with the caller `EPlayer`.
// Can return `none` if:
// 1. Caller `EPlayer` has already disconnected;