660 lines
16 KiB
Go
660 lines
16 KiB
Go
package entity
|
|
|
|
import (
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"eq2emu/internal/spells"
|
|
)
|
|
|
|
func TestNewEntity(t *testing.T) {
|
|
entity := NewEntity()
|
|
if entity == nil {
|
|
t.Fatal("NewEntity returned nil")
|
|
}
|
|
|
|
if entity.Spawn == nil {
|
|
t.Error("Expected Spawn to be initialized")
|
|
}
|
|
|
|
if entity.infoStruct == nil {
|
|
t.Error("Expected InfoStruct to be initialized")
|
|
}
|
|
|
|
if entity.spellEffectManager == nil {
|
|
t.Error("Expected SpellEffectManager to be initialized")
|
|
}
|
|
|
|
// Check default values
|
|
if entity.GetMaxSpeed() != 6.0 {
|
|
t.Errorf("Expected max speed 6.0, got %f", entity.GetMaxSpeed())
|
|
}
|
|
|
|
if entity.GetBaseSpeed() != 0.0 {
|
|
t.Errorf("Expected base speed 0.0, got %f", entity.GetBaseSpeed())
|
|
}
|
|
|
|
if entity.GetSpeedMultiplier() != 1.0 {
|
|
t.Errorf("Expected speed multiplier 1.0, got %f", entity.GetSpeedMultiplier())
|
|
}
|
|
|
|
// Check initial states
|
|
if entity.IsInCombat() {
|
|
t.Error("Expected entity to not be in combat initially")
|
|
}
|
|
|
|
if entity.IsCasting() {
|
|
t.Error("Expected entity to not be casting initially")
|
|
}
|
|
|
|
if entity.IsPetDismissing() {
|
|
t.Error("Expected pet to not be dismissing initially")
|
|
}
|
|
|
|
if entity.HasSeeInvisSpell() {
|
|
t.Error("Expected entity to not have see invisible spell initially")
|
|
}
|
|
|
|
if entity.HasSeeHideSpell() {
|
|
t.Error("Expected entity to not have see hidden spell initially")
|
|
}
|
|
}
|
|
|
|
func TestEntityIsEntity(t *testing.T) {
|
|
entity := NewEntity()
|
|
if !entity.IsEntity() {
|
|
t.Error("Expected IsEntity to return true")
|
|
}
|
|
}
|
|
|
|
func TestEntityInfoStruct(t *testing.T) {
|
|
entity := NewEntity()
|
|
|
|
stats := entity.GetInfoStruct()
|
|
if stats == nil {
|
|
t.Error("Expected InfoStruct to be initialized")
|
|
}
|
|
|
|
// Test setting a new info struct
|
|
newInfo := NewInfoStruct()
|
|
newInfo.SetName("Test Entity")
|
|
entity.SetInfoStruct(newInfo)
|
|
|
|
if entity.GetInfoStruct().GetName() != "Test Entity" {
|
|
t.Error("Expected info struct to be updated")
|
|
}
|
|
|
|
// Test that nil info struct is ignored
|
|
entity.SetInfoStruct(nil)
|
|
if entity.GetInfoStruct().GetName() != "Test Entity" {
|
|
t.Error("Expected info struct to remain unchanged when setting nil")
|
|
}
|
|
}
|
|
|
|
func TestEntityClient(t *testing.T) {
|
|
entity := NewEntity()
|
|
|
|
// Base Entity should return nil for GetClient
|
|
client := entity.GetClient()
|
|
if client != nil {
|
|
t.Error("Expected GetClient to return nil for base Entity")
|
|
}
|
|
}
|
|
|
|
func TestEntityCombatState(t *testing.T) {
|
|
entity := NewEntity()
|
|
|
|
// Initial state should be false
|
|
if entity.IsInCombat() {
|
|
t.Error("Expected entity to not be in combat initially")
|
|
}
|
|
|
|
// Set combat state to true
|
|
entity.SetInCombat(true)
|
|
if !entity.IsInCombat() {
|
|
t.Error("Expected entity to be in combat after setting to true")
|
|
}
|
|
|
|
// Set combat state to false
|
|
entity.SetInCombat(false)
|
|
if entity.IsInCombat() {
|
|
t.Error("Expected entity to not be in combat after setting to false")
|
|
}
|
|
}
|
|
|
|
func TestEntityCastingState(t *testing.T) {
|
|
entity := NewEntity()
|
|
|
|
// Initial state should be false
|
|
if entity.IsCasting() {
|
|
t.Error("Expected entity to not be casting initially")
|
|
}
|
|
|
|
// Set casting state to true
|
|
entity.SetCasting(true)
|
|
if !entity.IsCasting() {
|
|
t.Error("Expected entity to be casting after setting to true")
|
|
}
|
|
|
|
// Set casting state to false
|
|
entity.SetCasting(false)
|
|
if entity.IsCasting() {
|
|
t.Error("Expected entity to not be casting after setting to false")
|
|
}
|
|
}
|
|
|
|
func TestEntitySpeedMethods(t *testing.T) {
|
|
entity := NewEntity()
|
|
|
|
// Test max speed
|
|
entity.SetMaxSpeed(10.0)
|
|
if entity.GetMaxSpeed() != 10.0 {
|
|
t.Errorf("Expected max speed 10.0, got %f", entity.GetMaxSpeed())
|
|
}
|
|
|
|
// Test base speed
|
|
entity.SetBaseSpeed(5.0)
|
|
if entity.GetBaseSpeed() != 5.0 {
|
|
t.Errorf("Expected base speed 5.0, got %f", entity.GetBaseSpeed())
|
|
}
|
|
|
|
// Test speed multiplier
|
|
entity.SetSpeedMultiplier(2.0)
|
|
if entity.GetSpeedMultiplier() != 2.0 {
|
|
t.Errorf("Expected speed multiplier 2.0, got %f", entity.GetSpeedMultiplier())
|
|
}
|
|
|
|
// Test effective speed calculation with base speed set
|
|
effectiveSpeed := entity.CalculateEffectiveSpeed()
|
|
if effectiveSpeed != 10.0 { // 5.0 * 2.0
|
|
t.Errorf("Expected effective speed 10.0, got %f", effectiveSpeed)
|
|
}
|
|
|
|
// Test effective speed calculation with zero base speed (should use max speed)
|
|
entity.SetBaseSpeed(0.0)
|
|
effectiveSpeed = entity.CalculateEffectiveSpeed()
|
|
if effectiveSpeed != 20.0 { // 10.0 * 2.0
|
|
t.Errorf("Expected effective speed 20.0, got %f", effectiveSpeed)
|
|
}
|
|
}
|
|
|
|
func TestEntityPositionTracking(t *testing.T) {
|
|
entity := NewEntity()
|
|
|
|
// Test setting and getting last position
|
|
entity.SetLastPosition(100.0, 200.0, 300.0, 1.5)
|
|
x, y, z, heading := entity.GetLastPosition()
|
|
|
|
if x != 100.0 || y != 200.0 || z != 300.0 || heading != 1.5 {
|
|
t.Errorf("Expected position (100.0, 200.0, 300.0, 1.5), got (%f, %f, %f, %f)", x, y, z, heading)
|
|
}
|
|
|
|
// Test HasMoved with different positions
|
|
// Note: This would require setting the spawn's actual position, which depends on the spawn implementation
|
|
// For now, we just test that the method doesn't panic
|
|
moved := entity.HasMoved()
|
|
_ = moved // We can't easily test the actual movement detection without setting up spawn positions
|
|
}
|
|
|
|
func TestEntityStatCalculation(t *testing.T) {
|
|
entity := NewEntity()
|
|
info := entity.GetInfoStruct()
|
|
|
|
// Set base stats
|
|
info.SetStr(10.0)
|
|
info.SetSta(12.0)
|
|
info.SetAgi(8.0)
|
|
info.SetWis(15.0)
|
|
info.SetIntel(20.0)
|
|
|
|
// Test individual stat getters (these include bonuses from spell effects)
|
|
str := entity.GetStr()
|
|
if str < 10 {
|
|
t.Errorf("Expected strength >= 10, got %d", str)
|
|
}
|
|
|
|
sta := entity.GetSta()
|
|
if sta < 12 {
|
|
t.Errorf("Expected stamina >= 12, got %d", sta)
|
|
}
|
|
|
|
agi := entity.GetAgi()
|
|
if agi < 8 {
|
|
t.Errorf("Expected agility >= 8, got %d", agi)
|
|
}
|
|
|
|
wis := entity.GetWis()
|
|
if wis < 15 {
|
|
t.Errorf("Expected wisdom >= 15, got %d", wis)
|
|
}
|
|
|
|
intel := entity.GetIntel()
|
|
if intel < 20 {
|
|
t.Errorf("Expected intelligence >= 20, got %d", intel)
|
|
}
|
|
|
|
// Test primary stat calculation
|
|
primaryStat := entity.GetPrimaryStat()
|
|
if primaryStat < 20 {
|
|
t.Errorf("Expected primary stat >= 20 (intelligence is highest), got %d", primaryStat)
|
|
}
|
|
}
|
|
|
|
func TestEntityResistances(t *testing.T) {
|
|
entity := NewEntity()
|
|
info := entity.GetInfoStruct()
|
|
|
|
// Set base resistances
|
|
info.SetResistance("heat", 10)
|
|
info.SetResistance("cold", 15)
|
|
info.SetResistance("magic", 20)
|
|
info.SetResistance("mental", 25)
|
|
info.SetResistance("divine", 30)
|
|
info.SetResistance("disease", 35)
|
|
info.SetResistance("poison", 40)
|
|
|
|
// Test resistance getters
|
|
if entity.GetHeatResistance() != 10 {
|
|
t.Errorf("Expected heat resistance 10, got %d", entity.GetHeatResistance())
|
|
}
|
|
|
|
if entity.GetColdResistance() != 15 {
|
|
t.Errorf("Expected cold resistance 15, got %d", entity.GetColdResistance())
|
|
}
|
|
|
|
if entity.GetMagicResistance() != 20 {
|
|
t.Errorf("Expected magic resistance 20, got %d", entity.GetMagicResistance())
|
|
}
|
|
|
|
if entity.GetMentalResistance() != 25 {
|
|
t.Errorf("Expected mental resistance 25, got %d", entity.GetMentalResistance())
|
|
}
|
|
|
|
if entity.GetDivineResistance() != 30 {
|
|
t.Errorf("Expected divine resistance 30, got %d", entity.GetDivineResistance())
|
|
}
|
|
|
|
if entity.GetDiseaseResistance() != 35 {
|
|
t.Errorf("Expected disease resistance 35, got %d", entity.GetDiseaseResistance())
|
|
}
|
|
|
|
if entity.GetPoisonResistance() != 40 {
|
|
t.Errorf("Expected poison resistance 40, got %d", entity.GetPoisonResistance())
|
|
}
|
|
}
|
|
|
|
func TestEntityMaintainedSpells(t *testing.T) {
|
|
entity := NewEntity()
|
|
info := entity.GetInfoStruct()
|
|
|
|
// Set max concentration
|
|
info.SetMaxConcentration(10)
|
|
|
|
// Test adding maintained spell
|
|
success := entity.AddMaintainedSpell("Test Spell", 123, 60.0, 3)
|
|
if !success {
|
|
t.Error("Expected AddMaintainedSpell to succeed")
|
|
}
|
|
|
|
// Check concentration usage
|
|
if info.GetCurConcentration() != 3 {
|
|
t.Errorf("Expected current concentration 3, got %d", info.GetCurConcentration())
|
|
}
|
|
|
|
// Test getting maintained spell
|
|
effect := entity.GetMaintainedSpell(123)
|
|
if effect == nil {
|
|
t.Error("Expected to find maintained spell effect")
|
|
}
|
|
|
|
// Test adding spell that would exceed concentration
|
|
success = entity.AddMaintainedSpell("Another Spell", 124, 60.0, 8)
|
|
if success {
|
|
t.Error("Expected AddMaintainedSpell to fail when concentration exceeded")
|
|
}
|
|
|
|
// Test removing maintained spell
|
|
success = entity.RemoveMaintainedSpell(123)
|
|
if !success {
|
|
t.Error("Expected RemoveMaintainedSpell to succeed")
|
|
}
|
|
|
|
// Check concentration was returned
|
|
if info.GetCurConcentration() != 0 {
|
|
t.Errorf("Expected current concentration 0, got %d", info.GetCurConcentration())
|
|
}
|
|
|
|
// Test removing non-existent spell
|
|
success = entity.RemoveMaintainedSpell(999)
|
|
if success {
|
|
t.Error("Expected RemoveMaintainedSpell to fail for non-existent spell")
|
|
}
|
|
}
|
|
|
|
func TestEntitySpellEffects(t *testing.T) {
|
|
entity := NewEntity()
|
|
|
|
// Test adding spell effect
|
|
success := entity.AddSpellEffect(456, 789, 30.0)
|
|
if !success {
|
|
t.Error("Expected AddSpellEffect to succeed")
|
|
}
|
|
|
|
// Test removing spell effect
|
|
success = entity.RemoveSpellEffect(456)
|
|
if !success {
|
|
t.Error("Expected RemoveSpellEffect to succeed")
|
|
}
|
|
|
|
// Test removing non-existent spell effect
|
|
success = entity.RemoveSpellEffect(999)
|
|
if success {
|
|
t.Error("Expected RemoveSpellEffect to fail for non-existent spell")
|
|
}
|
|
}
|
|
|
|
func TestEntityDetrimentalSpells(t *testing.T) {
|
|
entity := NewEntity()
|
|
|
|
// Test adding detrimental spell
|
|
entity.AddDetrimentalSpell(789, 456, 45.0, 1)
|
|
|
|
// Test getting detrimental effect
|
|
effect := entity.GetDetrimentalEffect(789, 456)
|
|
if effect == nil {
|
|
t.Error("Expected to find detrimental spell effect")
|
|
}
|
|
|
|
// Test removing detrimental spell
|
|
success := entity.RemoveDetrimentalSpell(789, 456)
|
|
if !success {
|
|
t.Error("Expected RemoveDetrimentalSpell to succeed")
|
|
}
|
|
|
|
// Test removing non-existent detrimental spell
|
|
success = entity.RemoveDetrimentalSpell(999, 888)
|
|
if success {
|
|
t.Error("Expected RemoveDetrimentalSpell to fail for non-existent spell")
|
|
}
|
|
}
|
|
|
|
func TestEntityBonusSystem(t *testing.T) {
|
|
entity := NewEntity()
|
|
|
|
// Test adding skill bonus
|
|
entity.AddSkillBonus(123, 5, 15.0) // Skill ID 5, value 15.0
|
|
|
|
// Test adding stat bonus
|
|
entity.AddStatBonus(124, 1, 5.0) // Stat type 1 (STR), value 5.0
|
|
|
|
// Test calculating bonuses
|
|
entity.CalculateBonuses()
|
|
|
|
// The actual bonus calculation depends on the spell effect manager implementation
|
|
// We just test that the method doesn't panic
|
|
}
|
|
|
|
func TestEntityPetManagement(t *testing.T) {
|
|
entity := NewEntity()
|
|
pet := NewEntity()
|
|
|
|
// Test setting and getting summon pet
|
|
entity.SetPet(pet)
|
|
if entity.GetPet() != pet {
|
|
t.Error("Expected pet to be set correctly")
|
|
}
|
|
|
|
if pet.GetOwner() != entity.GetID() {
|
|
t.Error("Expected pet owner to be set correctly")
|
|
}
|
|
|
|
if pet.GetPetType() != PetTypeSummon {
|
|
t.Error("Expected pet type to be summon")
|
|
}
|
|
|
|
// Test charmed pet
|
|
charmedPet := NewEntity()
|
|
entity.SetCharmedPet(charmedPet)
|
|
if entity.GetCharmedPet() != charmedPet {
|
|
t.Error("Expected charmed pet to be set correctly")
|
|
}
|
|
|
|
if charmedPet.GetPetType() != PetTypeCharm {
|
|
t.Error("Expected pet type to be charm")
|
|
}
|
|
|
|
// Test deity pet
|
|
deityPet := NewEntity()
|
|
entity.SetDeityPet(deityPet)
|
|
if entity.GetDeityPet() != deityPet {
|
|
t.Error("Expected deity pet to be set correctly")
|
|
}
|
|
|
|
if deityPet.GetPetType() != PetTypeDeity {
|
|
t.Error("Expected pet type to be deity")
|
|
}
|
|
|
|
// Test cosmetic pet
|
|
cosmeticPet := NewEntity()
|
|
entity.SetCosmeticPet(cosmeticPet)
|
|
if entity.GetCosmeticPet() != cosmeticPet {
|
|
t.Error("Expected cosmetic pet to be set correctly")
|
|
}
|
|
|
|
if cosmeticPet.GetPetType() != PetTypeCosmetic {
|
|
t.Error("Expected pet type to be cosmetic")
|
|
}
|
|
|
|
// Test pet dismissing state
|
|
entity.SetPetDismissing(true)
|
|
if !entity.IsPetDismissing() {
|
|
t.Error("Expected pet to be dismissing")
|
|
}
|
|
|
|
entity.SetPetDismissing(false)
|
|
if entity.IsPetDismissing() {
|
|
t.Error("Expected pet to not be dismissing")
|
|
}
|
|
}
|
|
|
|
func TestEntityDeity(t *testing.T) {
|
|
entity := NewEntity()
|
|
|
|
// Test setting and getting deity
|
|
entity.SetDeity(5)
|
|
if entity.GetDeity() != 5 {
|
|
t.Errorf("Expected deity 5, got %d", entity.GetDeity())
|
|
}
|
|
}
|
|
|
|
func TestEntityDodgeChance(t *testing.T) {
|
|
entity := NewEntity()
|
|
|
|
// Test base dodge chance
|
|
dodgeChance := entity.GetDodgeChance()
|
|
if dodgeChance != 5.0 {
|
|
t.Errorf("Expected base dodge chance 5.0, got %f", dodgeChance)
|
|
}
|
|
}
|
|
|
|
func TestEntitySeeSpells(t *testing.T) {
|
|
entity := NewEntity()
|
|
|
|
// Test see invisible spell
|
|
entity.SetSeeInvisSpell(true)
|
|
if !entity.HasSeeInvisSpell() {
|
|
t.Error("Expected entity to have see invisible spell")
|
|
}
|
|
|
|
entity.SetSeeInvisSpell(false)
|
|
if entity.HasSeeInvisSpell() {
|
|
t.Error("Expected entity to not have see invisible spell")
|
|
}
|
|
|
|
// Test see hidden spell
|
|
entity.SetSeeHideSpell(true)
|
|
if !entity.HasSeeHideSpell() {
|
|
t.Error("Expected entity to have see hidden spell")
|
|
}
|
|
|
|
entity.SetSeeHideSpell(false)
|
|
if entity.HasSeeHideSpell() {
|
|
t.Error("Expected entity to not have see hidden spell")
|
|
}
|
|
}
|
|
|
|
func TestEntityCleanupMethods(t *testing.T) {
|
|
entity := NewEntity()
|
|
info := entity.GetInfoStruct()
|
|
|
|
// Set up some spell effects and concentration usage
|
|
info.SetMaxConcentration(10)
|
|
entity.AddMaintainedSpell("Test Spell 1", 123, 60.0, 3)
|
|
entity.AddMaintainedSpell("Test Spell 2", 124, 60.0, 2)
|
|
entity.AddSpellEffect(456, 789, 30.0)
|
|
|
|
// Verify concentration is used
|
|
if info.GetCurConcentration() != 5 {
|
|
t.Errorf("Expected current concentration 5, got %d", info.GetCurConcentration())
|
|
}
|
|
|
|
// Test deleting all spell effects
|
|
entity.DeleteSpellEffects(false)
|
|
|
|
// Verify concentration was returned
|
|
if info.GetCurConcentration() != 0 {
|
|
t.Errorf("Expected current concentration 0 after cleanup, got %d", info.GetCurConcentration())
|
|
}
|
|
|
|
// Test RemoveSpells
|
|
entity.AddMaintainedSpell("Test Spell 3", 125, 60.0, 2)
|
|
entity.RemoveSpells(false) // Remove all spells
|
|
|
|
if info.GetCurConcentration() != 0 {
|
|
t.Errorf("Expected current concentration 0 after RemoveSpells, got %d", info.GetCurConcentration())
|
|
}
|
|
}
|
|
|
|
func TestEntityProcessEffects(t *testing.T) {
|
|
entity := NewEntity()
|
|
|
|
// Test that ProcessEffects doesn't panic
|
|
entity.ProcessEffects()
|
|
}
|
|
|
|
func TestEntityClassSystemIntegration(t *testing.T) {
|
|
entity := NewEntity()
|
|
info := entity.GetInfoStruct()
|
|
|
|
// Test class getter/setter
|
|
info.SetClass1(5)
|
|
if entity.GetClass() != 5 {
|
|
t.Errorf("Expected class 5, got %d", entity.GetClass())
|
|
}
|
|
|
|
entity.SetClass(10)
|
|
if entity.GetClass() != 10 {
|
|
t.Errorf("Expected class 10, got %d", entity.GetClass())
|
|
}
|
|
|
|
// Test level getter
|
|
info.SetLevel(25)
|
|
if entity.GetLevel() != 25 {
|
|
t.Errorf("Expected level 25, got %d", entity.GetLevel())
|
|
}
|
|
}
|
|
|
|
func TestEntityConcurrency(t *testing.T) {
|
|
entity := NewEntity()
|
|
info := entity.GetInfoStruct()
|
|
info.SetMaxConcentration(20)
|
|
|
|
var wg sync.WaitGroup
|
|
numGoroutines := 10
|
|
|
|
// Test concurrent access to combat state
|
|
wg.Add(numGoroutines)
|
|
for i := 0; i < numGoroutines; i++ {
|
|
go func() {
|
|
defer wg.Done()
|
|
entity.SetInCombat(true)
|
|
_ = entity.IsInCombat()
|
|
entity.SetInCombat(false)
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
|
|
// Test concurrent access to casting state
|
|
wg.Add(numGoroutines)
|
|
for i := 0; i < numGoroutines; i++ {
|
|
go func() {
|
|
defer wg.Done()
|
|
entity.SetCasting(true)
|
|
_ = entity.IsCasting()
|
|
entity.SetCasting(false)
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
|
|
// Test concurrent spell effect operations
|
|
wg.Add(numGoroutines)
|
|
for i := 0; i < numGoroutines; i++ {
|
|
spellID := int32(1000 + i)
|
|
go func(id int32) {
|
|
defer wg.Done()
|
|
entity.AddSpellEffect(id, 123, 30.0)
|
|
time.Sleep(time.Millisecond) // Small delay to ensure some overlap
|
|
entity.RemoveSpellEffect(id)
|
|
}(spellID)
|
|
}
|
|
wg.Wait()
|
|
|
|
// Test concurrent maintained spell operations
|
|
wg.Add(numGoroutines)
|
|
for i := 0; i < numGoroutines; i++ {
|
|
spellID := int32(2000 + i)
|
|
go func(id int32) {
|
|
defer wg.Done()
|
|
if entity.AddMaintainedSpell("Concurrent Spell", id, 60.0, 1) {
|
|
time.Sleep(time.Millisecond)
|
|
entity.RemoveMaintainedSpell(id)
|
|
}
|
|
}(spellID)
|
|
}
|
|
wg.Wait()
|
|
}
|
|
|
|
func TestEntityControlEffects(t *testing.T) {
|
|
entity := NewEntity()
|
|
|
|
// Test has control effect - should work without panicking
|
|
// The actual implementation depends on the spell effect manager
|
|
hasStun := entity.HasControlEffect(spells.ControlEffectStun)
|
|
_ = hasStun // We can't easily test the actual value without setting up effects
|
|
}
|
|
|
|
func TestEntityConstants(t *testing.T) {
|
|
// Test pet type constants
|
|
if PetTypeSummon != 1 {
|
|
t.Errorf("Expected PetTypeSummon to be 1, got %d", PetTypeSummon)
|
|
}
|
|
|
|
if PetTypeCharm != 2 {
|
|
t.Errorf("Expected PetTypeCharm to be 2, got %d", PetTypeCharm)
|
|
}
|
|
|
|
if PetTypeDeity != 3 {
|
|
t.Errorf("Expected PetTypeDeity to be 3, got %d", PetTypeDeity)
|
|
}
|
|
|
|
if PetTypeCosmetic != 4 {
|
|
t.Errorf("Expected PetTypeCosmetic to be 4, got %d", PetTypeCosmetic)
|
|
}
|
|
|
|
// Test control effect constants (re-exported from spells package)
|
|
if spells.ControlEffectStun == 0 && spells.ControlEffectRoot == 0 {
|
|
t.Error("Control effect constants should be non-zero")
|
|
}
|
|
}
|