diff --git a/data/dfndata/items/misc/houseteleporters.dfn b/data/dfndata/items/misc/houseteleporters.dfn
new file mode 100644
index 000000000..c2c3df69d
--- /dev/null
+++ b/data/dfndata/items/misc/houseteleporters.dfn
@@ -0,0 +1,24 @@
+[base_house_teleporter]
+{
+get=base_item
+name=a house teleporter
+weight=100
+decay=1
+script=5080
+origin=hs
+movable=1
+sectionid=house_teleporter
+}
+
+[houseteleporter]
+{
+get=base_house_teleporter
+id=0x40BB
+}
+
+[chargedhouseteleporter]
+{
+get=base_house_teleporter
+id=0x574A
+custominttag=chargeable 1
+}
\ No newline at end of file
diff --git a/data/dictionaries/dictionary.CSY b/data/dictionaries/dictionary.CSY
index 4c66d7eec..e0321859c 100644
--- a/data/dictionaries/dictionary.CSY
+++ b/data/dictionaries/dictionary.CSY
@@ -5693,5 +5693,37 @@
30003=Šampion padl!
30004=Byl jsi odměněn Svitkem moci!
30005=Energie svatyně ubývá, protože nejsou přítomni žádní vyzyvatelé...
+// [30600-30650] Teleport domu
+30600=Pro propojení musí být tento objekt ve vašem batohu.
+30601=Pro propojení zaměřte druhý teleport v batohu.
+30602=Tento teleport je plně nabitý.
+30603=Pro dobití zaměřte předmět pro dobíjení v batohu.
+30604=Zabezpečení tohoto teleportu nelze změnit.
+30605=Zabezpečení teleportu nastaveno na: %s.
+30606=Tento teleport nelze přejmenovat.
+30607=Tento teleport musí být propojen, než bude možné jej přejmenovat.
+30608=Pro propojení musí být oba teleporty ve vašem batohu.
+30609=Pro propojení zaměřte druhý teleport v batohu.
+30610=Zdrojový teleport nenalezen.
+30611=Toto není teleport domu.
+30612=Musíte zaměřit druhý teleport.
+30613=Tyto teleporty jsou různých typů a nelze je propojit.
+30614=Oba teleporty musí být ve vašem batohu, aby se propojily.
+30615=Teleporty propojené.
+30616=Zaměřte se na dobíjecí předmět pro dobití teleportu.
+30617=Tento předmět musí být ve vašem batohu.
+30618=Teleport domu nelze dále nabíjet.
+30619=Dobíjecí předmět se rozpadne na prach, když posiluje Teleport domu.
+30620=Aby fungoval, musí být uzamčen v domě.
+30621=Musí být umístěn uvnitř domu.
+30622=Tento teleport může používat pouze majitel domu a spolumajitelé.
+30623=Toto nyní nemůžete použít.
+30624=Tento teleport není propojen.
+30625=Cílový teleport není správně umístěn.
+30626=V tomto teleportu nezbývají žádné náboje.
+30627=Tento teleport je plně nabitý.
+30628=Teleport nebyl nalezen.
+30629=Tento teleport musí být propojen, než bude možné jej přejmenovat.
+30630=Název teleportu nastaven na: %s
}
EOF
diff --git a/data/dictionaries/dictionary.ENG b/data/dictionaries/dictionary.ENG
index 7c6a7ef35..42440421e 100644
--- a/data/dictionaries/dictionary.ENG
+++ b/data/dictionaries/dictionary.ENG
@@ -5693,5 +5693,37 @@
30003=The Champion has fallen!
30004=You have been rewarded with a Scroll of Power!
30005=The shrine's energy wanes with no challengers present...
+// [30600-30650 ] House Teleporter
+30600=This must be in your backpack to link it.
+30601=Target the other teleporter in your backpack to link.
+30602=This teleporter is fully charged.
+30603=Target recharge item in your backpack to recharge
+30604=You cannot change this teleporter's security.
+30605=Teleporter security set to: %s
+30606=You cannot rename this teleporter.
+30607=This teleporter must be linked before it can be renamed.
+30608=To link, both teleporters must be in your backpack.
+30609=Target the other teleporter in your backpack to link.
+30610=Source teleporter not found.
+30611=That is not a house teleporter.
+30612=You must target the other teleporter.
+30613=These teleporters are different types and cannot be linked.
+30614=Both teleporters must be in your backpack to link.
+30615=Teleporters linked.
+30616=Target recharge item to recharge the teleporter.
+30617=This item must be in your backpack.
+30618=The House Teleporter cannot be charged any further.
+30619=The recharge item crumbles to dust as it strengthens the House Teleporter.
+30620=This must be locked down in a house to function.
+30621=This must be placed inside a house.
+30622=Only the house owner and co-owners may use this teleporter.
+30623=You cannot use that right now.
+30624=This teleporter is not linked.
+30625=The destination teleporter is not properly placed.
+30626=There are no charges left in this teleporter.
+30627=This teleporter is fully charged.
+30628=Teleporter not found.
+30629=This teleporter must be linked before it can be renamed.
+30630=Teleporter name set to: %s
}
EOF
diff --git a/data/dictionaries/dictionary.FRE b/data/dictionaries/dictionary.FRE
index 33f64c63b..ff1e163e6 100644
--- a/data/dictionaries/dictionary.FRE
+++ b/data/dictionaries/dictionary.FRE
@@ -5857,5 +5857,36 @@
30003=Le Champion est tombé !
30004=Vous avez reçu un Parchemin de Puissance !
30005=L'énergie du sanctuaire s'affaiblit en l'absence de challengers...
+// [30600-30650] Téléporteur de maison
+30600=Cet objet doit être dans votre sac à dos pour pouvoir le lier.
+30601=Ciblez l'autre téléporteur dans votre sac à dos pour le lier.
+30602=Ce téléporteur est entièrement chargé.
+30603=Ciblez l'objet de recharge dans votre sac à dos pour le recharger.
+30604=Vous ne pouvez pas modifier la sécurité de ce téléporteur.
+30605=Sécurité du téléporteur définie sur : %s
+30606=Vous ne pouvez pas renommer ce téléporteur.
+30607=Ce téléporteur doit être lié avant de pouvoir être renommé.
+30608=Pour les lier, les deux téléporteurs doivent être dans votre sac à dos.
+30609=Ciblez l'autre téléporteur dans votre sac à dos pour le lier.
+30610=Téléporteur source introuvable.
+30611=Ce n'est pas un téléporteur de maison.
+30612=Vous devez cibler l'autre téléporteur.
+30613=Ces téléporteurs sont de types différents et ne peuvent pas être liés.
+30614=Les deux téléporteurs doivent être dans votre sac à dos pour les lier.
+30615=Téléporteurs liés.
+30616=Ciblez l'objet de recharge pour recharger le téléporteur.
+30617=Cet objet doit être dans votre sac à dos.
+30618=Le téléporteur de maison ne peut pas être chargé davantage.
+30619=L'objet de recharge se désintègre en poussière en renforçant le téléporteur de maison.
+30620=Cet objet doit être verrouillé dans une maison pour fonctionner.
+30621=Cet objet doit être placé à l'intérieur d'une maison.
+30622=Seuls le propriétaire et les copropriétaires de la maison peuvent utiliser ce téléporteur.
+30623=Vous ne pouvez pas utiliser cet objet pour le moment.
+30624=Ce téléporteur n'est pas lié.
+30625=Le téléporteur de destination n'est pas correctement placé.
+30626=Il n'y a plus de charges dans ce téléporteur.
+30627=Ce téléporteur est entièrement chargé.
+30628=Téléporteur introuvable. 30629=Ce téléporteur doit être connecté avant de pouvoir être renommé.
+30630=Nom du téléporteur défini sur : %s
}
EOF
diff --git a/data/dictionaries/dictionary.GER b/data/dictionaries/dictionary.GER
index 8cec50d60..c200190d2 100644
--- a/data/dictionaries/dictionary.GER
+++ b/data/dictionaries/dictionary.GER
@@ -5693,5 +5693,37 @@
30003=Der Champion ist besiegt!
30004=Du wurdest mit einer Schriftrolle der Macht belohnt!
30005=Die Energie des Schreins schwindet, da keine Herausforderer anwesend sind...
+// [30600-30650] Hausteleporter
+30600=Dieses Objekt muss sich in Ihrem Rucksack befinden, um es zu verknüpfen.
+30601=Wählen Sie den anderen Teleporter in Ihrem Rucksack aus, um ihn zu verknüpfen.
+30602=Dieser Teleporter ist vollständig aufgeladen.
+30603=Wählen Sie ein Aufladeobjekt in Ihrem Rucksack aus, um den Teleporter aufzuladen.
+30604=Sie können die Sicherheitseinstellungen dieses Teleporters nicht ändern.
+30605=Sicherheitseinstellungen des Teleporters: %s
+30606=Sie können diesen Teleporter nicht umbenennen.
+30607=Dieser Teleporter muss verknüpft sein, bevor er umbenannt werden kann.
+30608=Um die Teleporter zu verknüpfen, müssen sich beide in Ihrem Rucksack befinden.
+30609=Wählen Sie den anderen Teleporter in Ihrem Rucksack aus, um ihn zu verknüpfen.
+30610=Quell-Teleporter nicht gefunden.
+30611=Das ist kein Hausteleporter.
+30612=Sie müssen den anderen Teleporter auswählen.
+30613=Diese Teleporter sind unterschiedlichen Typs und können nicht verknüpft werden.
+30614=Beide Teleporter müssen sich in Ihrem Rucksack befinden, um sie zu verknüpfen.
+30615=Teleporter verknüpft.
+30616=Wählen Sie ein Aufladeobjekt aus, um den Teleporter aufzuladen.
+30617=Dieses Objekt muss sich in Ihrem Rucksack befinden.
+30618=Der Hausteleporter kann nicht weiter aufgeladen werden.
+30619=Das Aufladeobjekt zerfällt zu Staub, während es den Hausteleporter verstärkt.
+30620=Dieses Objekt muss in einem Haus platziert und gesichert werden, damit es funktioniert.
+30621=Dieses Objekt muss in einem Haus platziert werden.
+30622=Nur der Hausbesitzer und die Miteigentümer dürfen diesen Teleporter benutzen.
+30623=Sie können das im Moment nicht verwenden.
+30624=Dieser Teleporter ist nicht verknüpft.
+30625=Der Ziel-Teleporter ist nicht richtig platziert.
+30626=Dieser Teleporter hat keine Ladungen mehr.
+30627=Dieser Teleporter ist vollständig aufgeladen.
+30628=Teleporter nicht gefunden.
+30629=Dieser Teleporter muss verknüpft werden, bevor er umbenannt werden kann.
+30630=Teleportername festgelegt auf: %s
}
EOF
diff --git a/data/dictionaries/dictionary.ITA b/data/dictionaries/dictionary.ITA
index fded04dd5..a13e01d97 100644
--- a/data/dictionaries/dictionary.ITA
+++ b/data/dictionaries/dictionary.ITA
@@ -5693,5 +5693,37 @@
30003=Il Campione è stato sconfitto!
30004=Hai ricevuto in ricompensa una Pergamena del Potere!
30005=L'energia del santuario si affievolisce in assenza di sfidanti...
+// [30600-30650] Teletrasporto per casa
+30600=Questo oggetto deve essere nel tuo zaino per poterlo collegare.
+30601=Seleziona l'altro teletrasporto nel tuo zaino per collegarli.
+30602=Questo teletrasporto è completamente carico.
+30603=Seleziona l'oggetto di ricarica nel tuo zaino per ricaricarlo.
+30604=Non puoi modificare la sicurezza di questo teletrasporto.
+30605=Sicurezza del teletrasporto impostata su: %s
+30606=Non puoi rinominare questo teletrasporto.
+30607=Questo teletrasporto deve essere collegato prima di poter essere rinominato.
+30608=Per il collegamento, entrambi i teletrasporti devono essere nel tuo zaino.
+30609=Seleziona l'altro teletrasporto nel tuo zaino per collegarli.
+30610=Teletrasporto di origine non trovato.
+30611=Questo non è un teletrasporto per casa.
+30612=Devi selezionare l'altro teletrasporto.
+30613=Questi teletrasporti sono di tipi diversi e non possono essere collegati.
+30614=Entrambi i teletrasporti devono essere nel tuo zaino per il collegamento.
+30615=Teletrasporti collegati.
+30616=Seleziona l'oggetto di ricarica per ricaricare il teletrasporto.
+30617=Questo oggetto deve essere nel tuo zaino.
+30618=Il teletrasporto per casa non può essere caricato ulteriormente.
+30619=L'oggetto di ricarica si sbriciola in polvere mentre potenzia il teletrasporto per casa.
+30620=Questo deve essere posizionato all'interno di una casa per funzionare.
+30621=Questo deve essere posizionato all'interno di una casa.
+30622=Solo il proprietario e i comproprietari della casa possono usare questo teletrasporto.
+30623=Non puoi usarlo in questo momento.
+30624=Questo teletrasporto non è collegato.
+30625=Il teletrasporto di destinazione non è posizionato correttamente.
+30626=Non ci sono più cariche in questo teletrasporto.
+30627=Questo teletrasporto è completamente carico.
+30628=Teletrasporto non trovato.
+30629=Questo teletrasporto deve essere collegato prima di poter essere rinominato.
+30630=Nome del teletrasporto impostato su: %s
}
EOF
diff --git a/data/dictionaries/dictionary.POL b/data/dictionaries/dictionary.POL
index 50af51c7c..41a2db28c 100644
--- a/data/dictionaries/dictionary.POL
+++ b/data/dictionaries/dictionary.POL
@@ -5693,5 +5693,37 @@
30003=Mistrz został pokonany!
30004=Otrzymałeś Zwoj mocy!
30005=Energia kapliczki słabnie z powodu braku pretendentów...
+// [30600-30650] Teleporter domowy
+30600=Aby połączyć teleportery, oba muszą znajdować się w twoim plecaku.
+30601=Wybierz drugi teleporter w swoim plecaku, aby go połączyć.
+30602=Ten teleporter jest w pełni naładowany.
+30603=Wybierz przedmiot do ładowania w swoim plecaku, aby naładować teleporter.
+30604=Nie możesz zmienić zabezpieczeń tego teleportera.
+30605=Zabezpieczenia teleportera ustawione na: %s
+30606=Nie możesz zmienić nazwy tego teleportera.
+30607=Ten teleporter musi być połączony, zanim będzie można zmienić jego nazwę.
+30608=Aby połączyć teleportery, oba muszą znajdować się w twoim plecaku.
+30609=Wybierz drugi teleporter w swoim plecaku, aby go połączyć.
+30610=Nie znaleziono teleportera źródłowego.
+30611=To nie jest teleporter domowy.
+30612=Musisz wybrać drugi teleporter.
+30613=Te teleportery są różnych typów i nie można ich połączyć.
+30614=Oba teleportery muszą znajdować się w twoim plecaku, aby je połączyć.
+30615=Teleportery połączone.
+30616=Wybierz przedmiot do ładowania, aby naładować teleporter.
+30617=Ten przedmiot musi znajdować się w twoim plecaku.
+30618=Teleportera domowego nie można już bardziej naładować.
+30619=Przedmiot do ładowania rozpada się w pył, wzmacniając teleporter domowy.
+30620=Aby działał, musi być umieszczony w domu.
+30621=Musi być umieszczony wewnątrz domu.
+30622=Tylko właściciel domu i współwłaściciele mogą korzystać z tego teleportera.
+30623=Nie możesz tego teraz użyć.
+30624=Ten teleporter nie jest połączony.
+30625=Teleporter docelowy nie jest prawidłowo umieszczony.
+30626=W tym teleporterze nie pozostały żadne ładunki.
+30627=Ten teleporter jest w pełni naładowany.
+30628=Nie znaleziono teleportera.
+30629=Ten teleport musi zostać połączony, zanim będzie można zmienić jego nazwę.
+30630=Nazwa teleportera została zmieniona na: %s
}
EOF
diff --git a/data/dictionaries/dictionary.PTG b/data/dictionaries/dictionary.PTG
index b6987987f..e4ee2f3b7 100644
--- a/data/dictionaries/dictionary.PTG
+++ b/data/dictionaries/dictionary.PTG
@@ -5693,5 +5693,37 @@
30003=O Campeão caiu!
30004=Foste recompensado com um Pergaminho de Poder!
30005=A energia do santuário diminui sem desafiantes presentes...
+// [30600-30650 ] Teletransportador de Casa
+30600=Este item deve estar na sua mochila para o ligar.
+30601=Selecione o outro teletransportador da sua mochila para o ligar.
+30602=Este teletransportador está totalmente carregado.
+30603=Selecione o item de recarga na sua mochila para o recarregar.
+30604=Não pode alterar a segurança deste teletransportador.
+30605=Segurança do teletransportador definida como: %s
+30606=Não pode renomear este teletransportador.
+30607=Este teletransportador necessita de ser ligado antes de poder ser renomeado.
+30608=Para ligar, ambos os teletransportadores devem estar na sua mochila.
+30609=Selecione o outro teletransportador da sua mochila para o ligar.
+30610=Teletransportador de origem não encontrado.
+30611=Este não é um teletransportador de casa.
+30612=Deve selecionar o outro teletransportador.
+30613=Estes teletransportadores são de tipos diferentes e não podem ser ligados.
+30614=Ambos os teletransportadores têm de estar na sua mochila para serem ligados.
+30615=Teletransportadores ligados.
+30616=Selecione o item de recarga para recarregar o teletransportador.
+30617=Este item deve estar na sua mochila.
+30618=O Teletransportador de Casa não pode ser recarregado.
+30619=O item de recarga desfaz-se em pó ao fortalecer o Teletransportador de Casa.
+30620=Este item deve ser instalado no interior de uma casa para funcionar.
+30621=Este item deve ser colocado dentro de uma casa.
+30622=Apenas o dono da casa e os coproprietários podem utilizar este teletransportador.
+30623=Não pode usar isso agora.
+30624=Este teletransportador não está ligado.
+30625=O teletransportador de destino não está corretamente posicionado.
+30626=Não restam cargas neste teletransportador.
+30627=Este teletransportador está totalmente carregado.
+30628=Teletransportador não encontrado.
+30629=Este teletransportador necessita de ser ligado antes de poder ser renomeado.
+30630=Nome do teletransportador definido como: %s
}
EOF
diff --git a/data/dictionaries/dictionary.SPA b/data/dictionaries/dictionary.SPA
index 34f5560ab..2afaf2ec1 100644
--- a/data/dictionaries/dictionary.SPA
+++ b/data/dictionaries/dictionary.SPA
@@ -5693,5 +5693,37 @@
30003=¡El Campeón ha caído!
30004=¡Has sido recompensado con un Pergamino de Poder!
30005=La energía del santuario disminuye al no haber desafiantes presentes...
+// [30600-30650] Teletransportador de casa
+30600=Esto debe estar en tu mochila para poder vincularlo.
+30601=Selecciona el otro teletransportador en tu mochila para vincularlos.
+30602=Este teletransportador está completamente cargado.
+30603=Selecciona el objeto de recarga en tu mochila para recargarlo.
+30604=No puedes cambiar la seguridad de este teletransportador.
+30605=Seguridad del teletransportador establecida en: %s
+30606=No puedes cambiar el nombre de este teletransportador.
+30607=Este teletransportador debe estar vinculado antes de poder cambiarle el nombre.
+30608=Para vincularlos, ambos teletransportadores deben estar en tu mochila.
+30609=Selecciona el otro teletransportador en tu mochila para vincularlos.
+30610=Teletransportador de origen no encontrado.
+30611=Eso no es un teletransportador de casa.
+30612=Debes seleccionar el otro teletransportador.
+30613=Estos teletransportadores son de tipos diferentes y no se pueden vincular.
+30614=Ambos teletransportadores deben estar en tu mochila para vincularlos.
+30615=Teletransportadores vinculados.
+30616=Selecciona el objeto de recarga para recargar el teletransportador.
+30617=Este objeto debe estar en tu mochila.
+30618=El teletransportador de casa no se puede cargar más.
+30619=El objeto de recarga se desintegra al fortalecer el teletransportador de casa.
+30620=Esto debe estar asegurado en una casa para funcionar.
+30621=Esto debe colocarse dentro de una casa.
+30622=Solo el propietario y los copropietarios de la casa pueden usar este teletransportador.
+30623=No puedes usar eso ahora mismo.
+30624=Este teletransportador no está vinculado.
+30625=El teletransportador de destino no está colocado correctamente.
+30626=No quedan cargas en este teletransportador.
+30627=Este teletransportador está completamente cargado.
+30628=Teletransportador no encontrado.
+30629=Este teletransportador debe estar vinculado antes de poder cambiarle el nombre.
+30630=Nombre del teletransportador establecido a: %s
}
EOF
diff --git a/data/dictionaries/dictionary.ZRO b/data/dictionaries/dictionary.ZRO
index a49b7576a..217f9ce6b 100644
--- a/data/dictionaries/dictionary.ZRO
+++ b/data/dictionaries/dictionary.ZRO
@@ -5693,5 +5693,37 @@
30003=The Champion has fallen!
30004=You have been rewarded with a Scroll of Power!
30005=The shrine's energy wanes with no challengers present...
+// [30600-30650 ] House Teleporter
+30600=This must be in your backpack to link it.
+30601=Target the other teleporter in your backpack to link.
+30602=This teleporter is fully charged.
+30603=Target recharge item in your backpack to recharge
+30604=You cannot change this teleporter's security.
+30605=Teleporter security set to: %s
+30606=You cannot rename this teleporter.
+30607=This teleporter must be linked before it can be renamed.
+30608=To link, both teleporters must be in your backpack.
+30609=Target the other teleporter in your backpack to link.
+30610=Source teleporter not found.
+30611=That is not a house teleporter.
+30612=You must target the other teleporter.
+30613=These teleporters are different types and cannot be linked.
+30614=Both teleporters must be in your backpack to link.
+30615=Teleporters linked.
+30616=Target recharge item to recharge the teleporter.
+30617=This item must be in your backpack.
+30618=The House Teleporter cannot be charged any further.
+30619=The recharge item crumbles to dust as it strengthens the House Teleporter.
+30620=This must be locked down in a house to function.
+30621=This must be placed inside a house.
+30622=Only the house owner and co-owners may use this teleporter.
+30623=You cannot use that right now.
+30624=This teleporter is not linked.
+30625=The destination teleporter is not properly placed.
+30626=There are no charges left in this teleporter.
+30627=This teleporter is fully charged.
+30628=Teleporter not found.
+30629=This teleporter must be linked before it can be renamed.
+30630=Teleporter name set to: %s
}
EOF
diff --git a/data/js/item/houseteleporter.js b/data/js/item/houseteleporter.js
new file mode 100644
index 000000000..bc9572894
--- /dev/null
+++ b/data/js/item/houseteleporter.js
@@ -0,0 +1,807 @@
+///
+// @ts-check
+
+const chargeItemIDReq = 0x1f60;
+const chargeItemSectionIDReq = "0x1f60";
+const chargeAmount = 5;
+const chargeMax = 1000;
+const teleporterIDReq = [0x40BB, 0x574A];
+const teleporterSectionIDReq = "house_teleporter";
+const coOwnHousesOnSameAccount = GetServerSetting( "CoOwnHousesOnSameAccount" );
+
+/** @type { ( item: Item | BaseObject ) => boolean } */
+function IsTeleporterItem( item )
+{
+ if( !item || !item.isItem )
+ return false;
+
+ if( item.sectionID == teleporterSectionIDReq )
+ return true;
+
+ for( var i = 0; i < teleporterIDReq.length; i++ )
+ {
+ if( item.id == teleporterIDReq[i] )
+ return true;
+ }
+ return false;
+}
+
+/** @type { ( teleItem: Item ) => Item | null } */
+function GetLinkedTeleporter( teleItem )
+{
+ if( !IsTeleporterItem( teleItem ))
+ return null;
+
+ var linkSer = teleItem.GetTag( "LinkSer" );
+ if( linkSer <= 0 )
+ return null;
+
+ var other = CalcItemFromSer( linkSer );
+ if( !IsTeleporterItem( other ))
+ return null;
+
+ return other;
+}
+
+/** @type { ( teleItem: Item ) => number } */
+function GetSharedCharges( teleItem )
+{
+ if( !IsTeleporterItem( teleItem ))
+ return 0;
+
+ var cur = teleItem.GetTag( "Charges" );
+ if( cur < 0 )
+ cur = 0;
+
+ var other = GetLinkedTeleporter( teleItem );
+ if( IsTeleporterItem( other ))
+ {
+ var otherCur = other.GetTag( "Charges" );
+ if( otherCur < 0 )
+ otherCur = 0;
+
+ if( otherCur > cur )
+ cur = otherCur;
+ }
+
+ if( cur > chargeMax )
+ cur = chargeMax;
+
+ return cur;
+}
+
+/** @type { ( teleItem: Item, newValue: number ) => void } */
+function SetSharedCharges( teleItem, newValue )
+{
+ if( !IsTeleporterItem( teleItem ))
+ return;
+
+ if( newValue < 0 )
+ newValue = 0;
+
+ if( newValue > chargeMax )
+ newValue = chargeMax;
+
+ teleItem.SetTag( "Charges", newValue );
+ teleItem.Refresh();
+
+ var other = GetLinkedTeleporter( teleItem );
+ if( IsTeleporterItem( other ))
+ {
+ other.SetTag( "Charges", newValue );
+ other.Refresh();
+ }
+}
+
+/** @type { ( teleporterA: Item, teleporterB: Item ) => void } */
+function SyncLinkedChargesOnLink( teleporterA, teleporterB )
+{
+ if( !IsTeleporterItem( teleporterA ) || !IsTeleporterItem( teleporterB ))
+ return;
+
+ var aCharge = teleporterA.GetTag( "Charges" );
+ var bCharge = teleporterB.GetTag( "Charges" );
+
+ if( aCharge < 0 )
+ aCharge = 0;
+ if( bCharge < 0 )
+ bCharge = 0;
+
+ var shared = aCharge + bCharge;
+ if( shared > chargeMax )
+ shared = chargeMax;
+
+ teleporterA.SetTag( "Charges", shared );
+ teleporterB.SetTag( "Charges", shared );
+ teleporterA.Refresh();
+ teleporterB.Refresh();
+}
+
+/** @type { ( mode: number ) => string } */
+function SecurityName( mode )
+{
+ if( mode == 1 )
+ return "Friends";
+ if( mode == 2 )
+ return "Anyone";
+ return "Owner/Co-Owners";
+}
+
+/** @type { ( pChar: Character, teleItem: Item ) => boolean } */
+function CanManageTeleporter( pChar, teleItem )
+{
+ if( !ValidateObject( pChar ) || !ValidateObject( teleItem ))
+ return false;
+
+ if( pChar.isGM )
+ return true;
+
+ var multiHouse = teleItem.multi;
+ if( !ValidateObject( multiHouse ))
+ return false;
+
+ if( multiHouse.IsOwner( pChar ))
+ return true;
+
+ if( multiHouse.IsOnOwnerList( pChar ))
+ return true;
+
+ if( coOwnHousesOnSameAccount && ValidateObject( multiHouse.owner ))
+ {
+ if( multiHouse.owner.accountNum == pChar.accountNum )
+ return true;
+ }
+
+ return false;
+}
+
+/** @type { ( pChar: Character, teleItem: Item ) => boolean } */
+function CanUseHouseTeleporter( pChar, teleItem )
+{
+ if( !ValidateObject( pChar ) || !ValidateObject( teleItem ))
+ return false;
+
+ if( pChar.isGM )
+ return true;
+
+ var multiHouse = teleItem.multi;
+ if( !ValidateObject( multiHouse ))
+ return false;
+
+ var mode = teleItem.GetTag( "Security" );
+ if( mode != 0 && mode != 1 && mode != 2 )
+ mode = 0;
+
+ if( multiHouse.IsOwner( pChar ))
+ return true;
+
+ if( multiHouse.IsOnOwnerList( pChar ))
+ return true;
+
+ if( coOwnHousesOnSameAccount && ValidateObject( multiHouse.owner ))
+ {
+ if( multiHouse.owner.accountNum == pChar.accountNum )
+ return true;
+ }
+
+ if( mode == 1 )
+ {
+ if( multiHouse.IsOnFriendList( pChar ))
+ return true;
+ return false;
+ }
+
+ if( mode == 2 )
+ return true;
+
+ return false;
+}
+
+/** @type { ( tSock: Socket, baseObj: BaseObject ) => boolean } */
+function onContextMenuRequest( socket, targObj )
+{
+ var pUser = socket.currentChar;
+ if( !ValidateObject( pUser ))
+ return true;
+
+ if( !IsTeleporterItem( targObj ))
+ return true;
+
+ var inPack = false;
+ if( ValidateObject( pUser.pack ))
+ {
+ var root0 = FindRootContainer( targObj, 0 );
+ inPack = ( ValidateObject( root0 ) && root0.isItem && root0.serial == pUser.pack.serial );
+ }
+
+ var canShowSecurity = false;
+ if( targObj.movable == 3 && ValidateObject( targObj.multi ) && CanManageTeleporter( pUser, targObj ))
+ canShowSecurity = true;
+
+ var canShowRename = false;
+ if( targObj.movable == 3 && ValidateObject( targObj.multi ) && CanManageTeleporter( pUser, targObj ))
+ {
+ var linkSerTmp = targObj.GetTag( "LinkSer" );
+ if( linkSerTmp > 0 )
+ {
+ var otherTmp = CalcItemFromSer( linkSerTmp );
+ if( IsTeleporterItem( otherTmp ) && otherTmp.movable == 3 )
+ canShowRename = true;
+ }
+ }
+
+ var numEntries = 1;
+ if( inPack )
+ numEntries++;
+
+ if( targObj.id == 0x574A && targObj.GetTag( "chargeable" ) == 1 )
+ numEntries++;
+
+ if( canShowSecurity )
+ numEntries++;
+
+ if( canShowRename )
+ numEntries++;
+
+ var offset = 12;
+ var toSend = new Packet();
+ var packetLen = 12 + ( numEntries * 8 );
+
+ toSend.ReserveSize( packetLen );
+ toSend.WriteByte( 0, 0xBF );
+ toSend.WriteShort( 1, packetLen );
+ toSend.WriteShort( 3, 0x14 );
+ toSend.WriteShort( 5, 0x0001 );
+ toSend.WriteLong( 7, targObj.serial );
+ toSend.WriteByte( 11, numEntries );
+
+ toSend.WriteShort( offset, 0x0101 );
+ toSend.WriteShort( offset += 2, 2140 );
+ toSend.WriteShort( offset += 2, 0x0020 );
+ toSend.WriteShort( offset += 2, 0x03E0 );
+ offset += 2;
+
+ if( inPack )
+ {
+ toSend.WriteShort( offset, 0x0102 );
+ toSend.WriteShort( offset += 2, 5119 );
+ toSend.WriteShort( offset += 2, 0x0020 );
+ toSend.WriteShort( offset += 2, 0x03E0 );
+ offset += 2;
+ }
+
+ if( targObj.id == 0x574A && targObj.GetTag( "chargeable" ) == 1 )
+ {
+ toSend.WriteShort( offset, 0x0103 );
+ toSend.WriteShort( offset += 2, 5042 );
+ toSend.WriteShort( offset += 2, 0x0020 );
+ toSend.WriteShort( offset += 2, 0x03E0 );
+ offset += 2;
+ }
+
+ if( canShowSecurity )
+ {
+ toSend.WriteShort( offset, 0x0104 );
+ toSend.WriteShort( offset += 2, 6203 );
+ toSend.WriteShort( offset += 2, 0x0020 );
+ toSend.WriteShort( offset += 2, 0x03E0 );
+ offset += 2;
+ }
+
+ if( canShowRename )
+ {
+ toSend.WriteShort( offset, 0x0105 );
+ toSend.WriteShort( offset += 2, 404 );
+ toSend.WriteShort( offset += 2, 0x0020 );
+ toSend.WriteShort( offset += 2, 0x03E0 );
+ offset += 2;
+ }
+
+ socket.Send( toSend );
+ toSend.Free();
+
+ return false;
+}
+
+/** @type { ( tSock: Socket, baseObj: BaseObject, popupEntry: number ) => boolean } */
+function onContextMenuSelect( socket, targObj, popupEntry )
+{
+ var pUser = socket.currentChar;
+ if( !ValidateObject( pUser ))
+ return true;
+
+ if( !IsTeleporterItem( targObj ))
+ return true;
+
+ if( popupEntry == 0x0101 )
+ {
+ var linkSer = targObj.GetTag( "LinkSer" );
+ var linked = false;
+ if( linkSer > 0 )
+ {
+ var other = CalcItemFromSer( linkSer );
+ if( IsTeleporterItem( other ))
+ linked = true;
+ }
+
+ if( targObj.id == 0x574A && targObj.GetTag( "chargeable" ) == 1 )
+ {
+ var charge = GetSharedCharges( targObj );
+ socket.SysMessage( "Teleporter: " + ( linked ? "Linked" : "Unlinked" ) + " | Charges: " + charge + "/" + chargeMax );
+ }
+ else
+ {
+ socket.SysMessage( "Teleporter: " + ( linked ? "Linked" : "Unlinked" ));
+ }
+ return false;
+ }
+
+ if( popupEntry == 0x0102 )
+ {
+ var inPackLink = false;
+ if( ValidateObject( pUser.pack ))
+ {
+ var root1 = FindRootContainer( targObj, 0 );
+ inPackLink = ( ValidateObject( root1 ) && root1.isItem && root1.serial == pUser.pack.serial );
+ }
+
+ if( !inPackLink )
+ {
+ socket.SysMessage( GetDictionaryEntry( 30600, socket.language ));
+ return false;
+ }
+
+ pUser.SetTempTag( "LinkSrcSer", targObj.serial );
+ socket.CustomTarget( 0, GetDictionaryEntry( 30601, socket.language )); // Target the other teleporter in your backpack to link.
+ targObj.Refresh();
+ return false;
+ }
+
+ if( popupEntry == 0x0103 )
+ {
+ if( targObj.GetTag( "chargeable" ) != 1 )
+ return false;
+
+ var cur = GetSharedCharges( targObj );
+ if( cur >= chargeMax )
+ {
+ socket.SysMessage( GetDictionaryEntry( 30602, socket.language ));
+ return false;
+ }
+
+ pUser.SetTempTag( "RechargeSer", targObj.serial );
+ socket.CustomTarget( 1, GetDictionaryEntry( 30603, socket.language )); // Target recharge item in your backpack to recharge.
+ targObj.Refresh();
+ return false;
+ }
+
+ if( popupEntry == 0x0104 )
+ {
+ if( !( targObj.movable == 3 && ValidateObject( targObj.multi ) && CanManageTeleporter( pUser, targObj )))
+ {
+ socket.SysMessage( GetDictionaryEntry( 30604, socket.language )); // You cannot change this teleporter's security
+ return false;
+ }
+
+ var curMode = targObj.GetTag( "Security" );
+ if( curMode != 0 && curMode != 1 && curMode != 2 )
+ curMode = 0;
+
+ var nextMode = curMode + 1;
+ if( nextMode > 2 )
+ nextMode = 0;
+
+ targObj.SetTag( "Security", nextMode );
+ socket.SysMessage( "Teleporter security set to: " + SecurityName( nextMode )); // Teleporter security set to: %s
+ targObj.Refresh();
+ return false;
+ }
+
+ if( popupEntry == 0x0105 )
+ {
+ if( !( targObj.movable == 3 && ValidateObject( targObj.multi ) && CanManageTeleporter( pUser, targObj )))
+ {
+ socket.SysMessage( GetDictionaryEntry( 30606, socket.language )); // You cannot rename this teleporter.
+ return false;
+ }
+
+ var linkSer2 = targObj.GetTag( "LinkSer" );
+ if( linkSer2 <= 0 )
+ {
+ socket.SysMessage( GetDictionaryEntry( 30607, socket.language )); // This teleporter must be linked before it can be renamed.
+ return false;
+ }
+
+ var other2 = CalcItemFromSer( linkSer2 );
+ if( !IsTeleporterItem( other2 ))
+ {
+ socket.SysMessage( GetDictionaryEntry( 30607, socket.language )); // This teleporter must be linked before it can be renamed.
+ return false;
+ }
+
+ pUser.SetTempTag( "RenameSer", targObj.serial );
+ pUser.SpeechInput( 1, targObj );
+ return false;
+ }
+
+ return false;
+}
+
+/** @type { ( user: Character, iUsing: Item ) => boolean } */
+function onUseChecked( pUser, iUsed )
+{
+ var pSocket = pUser.socket;
+ if( pSocket == null )
+ return false;
+
+ if( !IsTeleporterItem( iUsed ))
+ return false;
+
+ var inPackUse = false;
+ if( ValidateObject( pUser.pack ))
+ {
+ var root2 = FindRootContainer( iUsed, 0 );
+ inPackUse = ( ValidateObject( root2 ) && root2.isItem && root2.serial == pUser.pack.serial );
+ }
+
+ if( !inPackUse )
+ {
+ pSocket.SysMessage( GetDictionaryEntry( 30608, pSocket.language )); // To link, both teleporters must be in your backpack.
+ return false;
+ }
+
+ pUser.SetTempTag( "LinkSrcSer", iUsed.serial );
+ pSocket.CustomTarget( 0, GetDictionaryEntry( 30609, pSocket.language )); // Target the other teleporter in your backpack to link.
+ return false;
+}
+
+/** @type { ( tSock: Socket, target: Character | Item | null ) => void } */
+function onCallback0( socket, target )
+{
+ var pUser = socket.currentChar;
+ if( !ValidateObject( pUser ))
+ return;
+
+ if( socket.GetWord( 1 ))
+ return;
+
+ var srcSer = pUser.GetTempTag( "LinkSrcSer" );
+ var teleporterA = CalcItemFromSer( srcSer );
+ var teleporterB = target;
+
+ if( !IsTeleporterItem( teleporterA ))
+ {
+ socket.SysMessage( GetDictionaryEntry( 30610, socket.language )); // Source teleporter not found.
+ return;
+ }
+ if( !IsTeleporterItem( teleporterB ))
+ {
+ socket.SysMessage( GetDictionaryEntry( 30611, socket.language )); // That is not a house teleporter.
+ return;
+ }
+ if( teleporterA.serial == teleporterB.serial )
+ {
+ socket.SysMessage( GetDictionaryEntry( 30612, socket.language )); // You must target the other teleporter.
+ return;
+ }
+ if( teleporterA.id != teleporterB.id )
+ {
+ socket.SysMessage( GetDictionaryEntry( 30613, socket.language )); // These teleporters are different types and cannot be linked.
+ return;
+ }
+
+ var aInPack = false;
+ var bInPack = false;
+
+ if( ValidateObject( pUser.pack ))
+ {
+ var rootA = FindRootContainer( teleporterA, 0 );
+ var rootB = FindRootContainer( teleporterB, 0 );
+
+ aInPack = ( ValidateObject( rootA ) && rootA.isItem && rootA.serial == pUser.pack.serial );
+ bInPack = ( ValidateObject( rootB ) && rootB.isItem && rootB.serial == pUser.pack.serial );
+ }
+
+ if( !aInPack || !bInPack )
+ {
+ socket.SysMessage( GetDictionaryEntry( 30614, socket.language )); // Both teleporters must be in your backpack to link.
+ return;
+ }
+
+ UnlinkOther( teleporterA, teleporterB.serial );
+ UnlinkOther( teleporterB, teleporterA.serial );
+
+ teleporterA.SetTag( "LinkSer", teleporterB.serial );
+ teleporterB.SetTag( "LinkSer", teleporterA.serial );
+
+ // Make both share the same charge pool immediately
+ if( teleporterA.GetTag( "chargeable" ) == 1 && teleporterB.GetTag( "chargeable" ) == 1 )
+ SyncLinkedChargesOnLink( teleporterA, teleporterB );
+
+ socket.SysMessage( GetDictionaryEntry( 30615, socket.language )); // Teleporters linked.
+}
+
+/** @type { ( tSock: Socket, target: Character | Item | null ) => void } */
+function onCallback1( socket, target )
+{
+ var pUser = socket.currentChar;
+ if( !ValidateObject( pUser ))
+ return;
+
+ if( socket.GetWord( 1 ))
+ return;
+
+ var teleporterSerial = pUser.GetTempTag( "RechargeSer" );
+ var teleporter = CalcItemFromSer( teleporterSerial );
+ if( !ValidateObject( teleporter ) || !teleporter.isItem || teleporter.GetTag( "chargeable" ) != 1 )
+ return;
+
+ var scroll = target;
+ if( !( ValidateObject( scroll ) && scroll.isItem && ( scroll.id == chargeItemIDReq || scroll.sectionID == chargeItemSectionIDReq )))
+ {
+ socket.SysMessage( GetDictionaryEntry( 30616, socket.language ));
+ return;
+ }
+
+ var scrollInPack = false;
+ if( ValidateObject( pUser.pack ))
+ {
+ var root3 = FindRootContainer( scroll, 0 );
+ scrollInPack = ( ValidateObject( root3 ) && root3.isItem && root3.serial == pUser.pack.serial );
+ }
+
+ if( !scrollInPack )
+ {
+ socket.SysMessage( GetDictionaryEntry( 30617, socket.language ));// This item must be in your backpack.
+ return;
+ }
+
+ var cur = GetSharedCharges( teleporter );
+ var stackAmt = scroll.amount;
+ if( stackAmt < 1 )
+ stackAmt = 1;
+
+ var remaining = chargeMax - cur;
+ if( remaining <= 0 )
+ {
+ socket.SysMessage( GetDictionaryEntry( 30618, socket.language ));// The House Teleporter cannot be charged any further.
+ return;
+ }
+
+ var needScrolls = Math.ceil( remaining / chargeAmount );
+ var useScrolls = needScrolls;
+
+ if( useScrolls > stackAmt )
+ useScrolls = stackAmt;
+ if( useScrolls < 1 )
+ useScrolls = 1;
+
+ if( stackAmt > useScrolls )
+ scroll.amount = stackAmt - useScrolls;
+ else
+ scroll.Delete();
+
+ cur += ( useScrolls * chargeAmount );
+ if( cur > chargeMax )
+ cur = chargeMax;
+
+ SetSharedCharges( teleporter, cur );
+
+ socket.SysMessage( GetDictionaryEntry( 30619, socket.language ));// The recharge item crumbles to dust as it strengthens the House Teleporter.
+}
+
+/** @type { ( tile: Item, keepSer: number ) => void } */
+function UnlinkOther( tile, keepSer )
+{
+ var old = tile.GetTag( "LinkSer" );
+ tile.SetTag( "LinkSer", null );
+
+ if( old > 0 && old != keepSer )
+ {
+ var oldtele = CalcItemFromSer( old );
+ if( IsTeleporterItem( oldtele ))
+ {
+ oldtele.SetTag( "LinkSer", null );
+
+ if( oldtele.GetTag( "chargeable" ) == 1 )
+ {
+ oldtele.SetTag( "Charges", 0 );
+ oldtele.Refresh();
+ }
+ }
+ }
+}
+
+/** @type { ( targSock: Socket, objColliding: Character, objCollideWith: BaseObject ) => boolean } */
+function onCollide( trgSock, pColliding, objCollidedWith )
+{
+ if( !ValidateObject( pColliding ) || !pColliding.isChar )
+ return false;
+
+ if( !IsTeleporterItem( objCollidedWith ))
+ return false;
+
+ if( trgSock == null )
+ return false;
+
+ if( objCollidedWith.movable != 3 )
+ {
+ trgSock.SysMessage( GetDictionaryEntry( 30620, trgSock.language )); // This must be locked down in a house to function.
+ return false;
+ }
+
+ var iMulti = FindMulti( objCollidedWith.x, objCollidedWith.y, objCollidedWith.z, objCollidedWith.worldnumber, objCollidedWith.instanceID );
+ var insideHouse = ( ValidateObject( iMulti ) && iMulti.IsInMulti( objCollidedWith ));
+
+ if( !insideHouse )
+ {
+ trgSock.SysMessage( GetDictionaryEntry( 30621, trgSock.language )); // This must be placed inside a house.
+ return false;
+ }
+
+ if( !CanUseHouseTeleporter( pColliding, objCollidedWith ))
+ {
+ trgSock.SysMessage( GetDictionaryEntry( 30622, trgSock.language )); // Only the house owner and co-owners may use this teleporter.
+ return false;
+ }
+
+ if( pColliding.dead || pColliding.criminal )
+ {
+ trgSock.SysMessage( GetDictionaryEntry( 30623, trgSock.language )); // You cannot use that right now.
+ return false;
+ }
+
+ var linkSer = objCollidedWith.GetTag( "LinkSer" );
+ if( linkSer <= 0 )
+ {
+ trgSock.SysMessage( GetDictionaryEntry( 30624, trgSock.language )); // This teleporter is not linked.
+ return false;
+ }
+
+ var other = CalcItemFromSer( linkSer );
+ if( !IsTeleporterItem( other ))
+ {
+ trgSock.SysMessage( GetDictionaryEntry( 30624, trgSock.language )); // This teleporter is not linked.
+ return false;
+ }
+
+ if( other.movable != 3 )
+ {
+ trgSock.SysMessage( GetDictionaryEntry( 30625, trgSock.language )); // The destination teleporter is not properly placed.
+ return false;
+ }
+
+ var destMulti = FindMulti( other.x, other.y, other.z, other.worldnumber, other.instanceID );
+ var destInsideHouse = ( ValidateObject( destMulti ) && destMulti.IsInMulti( other ));
+ if( !destInsideHouse )
+ {
+ trgSock.SysMessage( GetDictionaryEntry( 30625, trgSock.language ));// The destination teleporter is not properly placed.
+ return false;
+ }
+
+ if( objCollidedWith.GetTag( "chargeable" ) == 1 )
+ {
+ var charge = GetSharedCharges( objCollidedWith );
+ if( charge <= 0 )
+ {
+ trgSock.SysMessage( GetDictionaryEntry( 30626, trgSock.language )); // There are no charges left in this teleporter.
+ return false;
+ }
+
+ SetSharedCharges( objCollidedWith, charge - 1 );
+ }
+
+ // Collect nearby following pets before teleporting player
+ var petsToMove = [];
+ var followerList = pColliding.GetFollowerList();
+ for( var i = 0; i < followerList.length; i++ )
+ {
+ var tempFollower = followerList[i];
+ if( ValidateObject( tempFollower ) && tempFollower.wandertype == 1 && tempFollower.InRange( pColliding, 24 ))
+ petsToMove.push( tempFollower );
+ }
+
+ pColliding.Teleport( other.x, other.y, other.z, other.worldnumber, other.instanceID );
+
+ for( var j = 0; j < petsToMove.length; j++ )
+ {
+ if( ValidateObject( petsToMove[j] ))
+ {
+ petsToMove[j].Teleport( other.x, other.y, other.z, other.worldnumber, other.instanceID );
+ petsToMove[j].Follow( pColliding );
+ }
+ }
+
+ return false;
+}
+
+/** @type { ( myChar: Character, myItem: Item, mySpeech: string, mySpeechId: number ) => void } */
+function onSpeechInput( pUser, targObj, pSpeech, pSpeechID )
+{
+ var pSocket = pUser.socket;
+ if( !pSocket )
+ return;
+
+ if( pSpeechID != 1 )
+ return;
+
+ if( pSpeech == null )
+ return;
+
+ pSpeech = ( "" + pSpeech ).replace(/^\s+|\s+$/g, "" );
+ if( pSpeech.length <= 0 )
+ {
+ pSocket.SysMessage( GetDictionaryEntry( 9270, pSocket.language ));// too short / none entered
+ return;
+ }
+ if( pSpeech.length > 60 )
+ {
+ pSocket.SysMessage( GetDictionaryEntry( 9271, pSocket.language ));// too long (we'll reuse)
+ return;
+ }
+
+ var srcSer = pUser.GetTempTag( "RenameSer" );
+ var teleA = CalcItemFromSer( srcSer );
+ if( !IsTeleporterItem( teleA ))
+ {
+ pSocket.SysMessage( GetDictionaryEntry( 30628, pSocket.language )); // Teleporter not found.
+ return;
+ }
+
+ var linkSer = teleA.GetTag( "LinkSer" );
+ if( linkSer <= 0 )
+ {
+ pSocket.SysMessage( GetDictionaryEntry( 30629, pSocket.language ));// This teleporter must be linked before it can be renamed.
+ return;
+ }
+
+ var teleB = CalcItemFromSer( linkSer );
+ if( !IsTeleporterItem( teleB ))
+ {
+ pSocket.SysMessage( GetDictionaryEntry( 30629, pSocket.language ));// This teleporter must be linked before it can be renamed.
+ return;
+ }
+
+ teleA.name = pSpeech;
+ teleB.name = pSpeech;
+
+ teleA.Refresh();
+ teleB.Refresh();
+
+ pSocket.SysMessage( "Teleporter name set to: " + pSpeech );
+}
+
+/** @type { ( myObj: BaseObject, pSocket: Socket ) => string } */
+function onTooltip( myObj, pSocket )
+{
+ if( !ValidateObject( myObj ) || !myObj.isItem )
+ return "";
+
+ if( !IsTeleporterItem( myObj ))
+ return "";
+
+ var parts = [];
+ var secMode = myObj.GetTag( "Security" );
+ if( secMode != 0 && secMode != 1 && secMode != 2 )
+ secMode = 0;
+
+ parts.push( "Security: " + SecurityName( secMode ));
+
+ if( myObj.id == 0x574A && myObj.GetTag( "chargeable" ) == 1 )
+ {
+ var charge = GetSharedCharges( myObj );
+ parts.push( "Charges: " + charge + "/" + chargeMax );
+ }
+
+ var linkSer = myObj.GetTag( "LinkSer" );
+ var linked = false;
+ if( linkSer > 0 )
+ {
+ var other = CalcItemFromSer( linkSer );
+ if( IsTeleporterItem( other ))
+ linked = true;
+ }
+ parts.push( "Link: " + ( linked ? "Linked" : "Unlinked" ));
+
+ return parts.join( "
" );
+}
\ No newline at end of file
diff --git a/data/js/jse_fileassociations.scp b/data/js/jse_fileassociations.scp
index 5b993348f..2f86dfe54 100644
--- a/data/js/jse_fileassociations.scp
+++ b/data/js/jse_fileassociations.scp
@@ -327,6 +327,7 @@
5063=item/recipescroll.js
5064=item/runicatlas.js
5070=item/dawns_music_box.js
+5080=item/houseteleporter.js
5300=item/etherealstatuettes.js
5301=npc/pets/dismountetherealmount.js