From 431e2d5ddcc33b571d073d223efe26c5d82c8307 Mon Sep 17 00:00:00 2001 From: Marisa Kirisame Date: Sun, 13 Sep 2020 14:39:24 +0200 Subject: [PATCH] Various optimizations and a couple changes: - Lightweight Tick() for smoke and debris (has some glitches I gotta figure out). - Add option for buckshot to gib (on by default). - Clips are replaced by shells again. - Increased look sensitivity when crouching with Silver Bullet. - Fix 3D floor handling of nonsolids. - Fix combat trackers not updating their position when an actor changes height while stationary (e.g.: crouching players). --- README.md | 2 +- cvarinfo.txt | 1 + language.def_menu | 10 +- language.es_menu | 10 +- language.version | 2 +- menudef.txt | 4 + zscript/swwm_blazeit.zsc | 4 + zscript/swwm_blod.zsc | 3 +- zscript/swwm_cbt.zsc | 2 +- zscript/swwm_common.zsc | 465 ++++++++++++++++++++++++++++++++--- zscript/swwm_danmaku.zsc | 2 + zscript/swwm_handler.zsc | 23 +- zscript/swwm_jackhammer.zsc | 2 + zscript/swwm_powerup.zsc | 2 + zscript/swwm_shot.zsc | 8 +- zscript/swwm_sparkyboi.zsc | 2 + zscript/swwm_thiccboolet.zsc | 2 +- zscript/swwm_thinkers.zsc | 27 +- zscript/swwm_utility.zsc | 1 + 19 files changed, 514 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index f7270d3c6..7b5e27bc8 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ Coming from the **Doom** modding side of things, this gun really packs a punch, The **Spreadgun** uses 10 gauge ammunition, apparently just for the sake of making it feel more powerful due to the increased recoil. -### 10Ga shells ~ Replaces Shells, Ethereal Arrows, Chaos Device/Banishment Device +### 10Ga shells ~ Replaces Clip / Shells, Wand Crystal / Ethereal Arrows, Chaos Device / Banishment Device ![](docimg/shells.png) Available in the following types, with varying chances of appearing depending diff --git a/cvarinfo.txt b/cvarinfo.txt index 2aa51935f..4fe3bd733 100644 --- a/cvarinfo.txt +++ b/cvarinfo.txt @@ -81,3 +81,4 @@ server bool swwm_simplefog = false; // simplified teleport fogs (useful to spee user bool swwm_bigtags = false; // use a bigger font for targeter tags user bool swwm_intermusic = false; // use original intermission music server bool swwm_enemydrops = false; // allow enemies to drop ammo and weapons +server bool swwm_shotgib = true; // buckshot can gib (some people don't like this for some reason, so it's an option now) diff --git a/language.def_menu b/language.def_menu index be8eae10c..1e697e783 100644 --- a/language.def_menu +++ b/language.def_menu @@ -86,7 +86,7 @@ SWWM_INTERTYPE = "Intermission Art"; SWWM_INTERDEF = "N/A"; SWWM_INTERART = "Fanart"; SWWM_INTER4KOMA = "4Koma"; -SWWM_BALLUSE = "Lead Balls can activate switches"; +SWWM_BALLUSE = "Lead Balls can Activate Switches"; SWWM_BOSSBAR = "Show Boss Healthbars"; SWWM_BTITLE = "Balance Options"; SWWM_BOSSENHANCE = "Enhance Vanilla Bosses"; @@ -115,6 +115,9 @@ SWWM_YNYKRONALERT = "Cap Ynykron Alert"; SWWM_MENUPAUSE = "Pause on Demolitionist Menu"; SWWM_FTITLE = "Fun Options"; SWWM_FUNTAGS = "Monster Renaming"; +SWWM_OMNIBUST = "Omni-Busting"; +SWWM_SUPERFUEL = "Unlimited Fuel"; +SWWM_PARTYTIME = "Super Fun Party Time"; SWWM_SIMPLEFOG = "Simplified Teleport Fog"; SWWM_BIGTAGS = "Use Large Font for Names"; SWWM_INTERMUSIC = "Use Original Intermission Music"; @@ -123,6 +126,7 @@ SWWM_MAXGIBS = "Gib Limit"; SWWM_MAXCASINGS = "Casing Limit"; SWWM_MAXDEBRIS = "Debris Limit"; SWWM_ENEMYDROPS = "Enemies Drop Weapons and Ammo"; +SWWM_SHOTGIB = "Buckshot can Gib"; TOOLTIP_SWWM_VOICETYPE = "Sets the voice pack for the player."; TOOLTIP_SWWM_MUTEVOICE = "Control what gets muted, if you'd rather have a more silent protagonist."; TOOLTIP_SWWM_FLASHSTRENGTH = "Screen flashes usually happen when firing some weapons, you can lower this if these effects are harmful for you."; @@ -179,6 +183,9 @@ TOOLTIP_SWWM_NOTRACK = "Completely disables combat tracking (responsible for hea TOOLTIP_SWWM_YNYKRONALERT = "By default the Ynykron wakes all monsters in the map. This may sometimes cause issues (and would destroy your framerate with huge monstercounts)."; TOOLTIP_SWWM_MENUPAUSE = "In single player, the Demolitionist Menu by default will pause everything. You can disable this if you want a more Souls-like experience (or if you want to have a read while actively waiting for some looooooooong elevator or other timed event)."; TOOLTIP_SWWM_FUNTAGS = "Replaces the names of various vanilla monsters with funny alternatives."; +TOOLTIP_SWWM_OMNIBUST = "Allows any weapon to potentially bust walls."; +TOOLTIP_SWWM_SUPERFUEL = "Your fuel never runs out."; +TOOLTIP_SWWM_PARTYTIME = "Killed enemies shoot out a cloud of confetti."; TOOLTIP_SWWM_SIMPLEFOG = "Replaces particle-based teleport fogs with simple sprites. Useful if they cause performance issues."; TOOLTIP_SWWM_BIGTAGS = "By default name labels on healthbars are shown with a small, compact font. Enabling this will use a bigger one."; TOOLTIP_SWWM_INTERMUSIC = "If enabled, intermission screens will use the original music from the current IWAD or map pack."; @@ -187,6 +194,7 @@ TOOLTIP_SWWM_MAXGIBS = "Caps the maximum amount of gibs. Surpassing this limit w TOOLTIP_SWWM_MAXCASINGS = "Caps the maximum amount of casings and spent magazines. Surpassing this limit will cause the excess to fade out."; TOOLTIP_SWWM_MAXDEBRIS = "Caps the maximum amount of rubble from explosions and others. Surpassing this limit will cause the excess to fade out."; TOOLTIP_SWWM_ENEMYDROPS = "If enabled, enemies will drop weapons and ammo like they would in vanilla. This is not recommended as it easily breaks progression."; +TOOLTIP_SWWM_SHOTGIB = "Some people don't like this for some reason, so here it is as an option."; // knowledge base SWWM_COMINGSOON = "(coming soon)"; SWWM_MISSTAB = "Mission"; diff --git a/language.es_menu b/language.es_menu index 412ded4bc..405cdea6d 100644 --- a/language.es_menu +++ b/language.es_menu @@ -83,7 +83,7 @@ SWWM_REVIVECOOLDOWN = "Tiempo de recarga de Reinicio"; SWWM_UNLIMITED = "Ilimitado"; SWWM_NONE = "Ninguno"; SWWM_INTERTYPE = "Arte de Intermisión"; -SWWM_BALLUSE = "Las Bolas de Plomo activan botones"; +SWWM_BALLUSE = "Las Bolas de Plomo Activan Botones"; SWWM_BOSSBAR = "Mostrar Barras de Salud de Bosses"; SWWM_BTITLE = "Opciones de Balance"; SWWM_BOSSENHANCE = "Mejorar Bosses Vanilla"; @@ -112,6 +112,9 @@ SWWM_YNYKRONALERT = "Limitar Alerta de Ynykron"; SWWM_MENUPAUSE = "Pausar en Menú de Demolicionista"; SWWM_FTITLE = "Opciones Divertidas"; SWWM_FUNTAGS = "Renombrar Monstruos"; +SWWM_OMNIBUST = "Omni-Reventamiento"; +SWWM_SUPERFUEL = "Combustible Ilimitado"; +SWWM_PARTYTIME = "Modo Fiesta Tope"; SWWM_SIMPLEFOG = "Efecto de Teletransporte Simple" SWWM_BIGTAGS = "Usar Fuente Grande para Nombres"; SWWM_INTERMUSIC = "Usar Música de Intermisión Original"; @@ -120,6 +123,7 @@ SWWM_MAXGIBS = "Límite de Vísceras"; SWWM_MAXCASINGS = "Límite de Casquillos"; SWWM_MAXDEBRIS = "Límite de Escombros"; SWWM_ENEMYDROPS = "Enemigos Sueltan Armas y Munición"; +SWWM_SHOTGIB = "Los Perdigones pueden Desviscerar"; TOOLTIP_SWWM_VOICETYPE = "Selecciona el pack de voz para el jugador."; TOOLTIP_SWWM_MUTEVOICE = "Controla lo que se mutea, si prefieres tener un protagonista más silencioso."; TOOLTIP_SWWM_FLASHSTRENGTH = "Los destellos en pantalla suelen ocurrir al disparar algunas armas, puedes reducirlo si este tipo de effectos te causan malestar."; @@ -176,6 +180,9 @@ TOOLTIP_SWWM_NOTRACK = "Desactiva completamente el rastreo de combate (responsab TOOLTIP_SWWM_YNYKRONALERT = "Por defecto el Ynykron alerta a todos los monstruos en el mapa. Esto puede causar problemas a veces (o destruir tus frames con números enormes de monstruos)."; TOOLTIP_SWWM_MENUPAUSE = "En modo de un jugador, el Menú de Demolicionista por defecto paus a todo. Puedes desactivar esto si quieres una experiencia más Souls-like (o si quieres leer un poco mientras esperas activamente algun ascensor leeeeeeento o algún otro evento temporizado)." TOOLTIP_SWWM_FUNTAGS = "Reemplaza los nombres de monstruos vanilla por alternativas graciosas."; +TOOLTIP_SWWM_OMNIBUST = "Permite que cualquier arma puede potencialmente reventar paredes."; +TOOLTIP_SWWM_SUPERFUEL = "Tu combustible no se agota nunca."; +TOOLTIP_SWWM_PARTYTIME = "Los enemigos expulsan una nube de confetti al morir."; TOOLTIP_SWWM_SIMPLEFOG = "Reemplaza los efectos de teletransporte basados en partículas con simples sprites. Útil si causan problemas de rendimiento."; TOOLTIP_SWWM_BIGTAGS = "Por defecto los nombres de las barras de vida usan una fuente pequeña. Activando esta opción, se cambiará por una mayor."; TOOLTIP_SWWM_INTERMUSIC = "Al activar, las pantallas de intermisión usan la música original del IWAD o pack de mapas usado."; @@ -184,6 +191,7 @@ TOOLTIP_SWWM_MAXGIBS = "Limita la cantidad máxima de vísceras. Sobrepasar este TOOLTIP_SWWM_MAXCASINGS = "Limita la cantidad máxima de casquillos y cargadores usados. Sobrepasar este límite causará que el exceso se desvanezca."; TOOLTIP_SWWM_MAXDEBRIS = "Limita la cantidad máxima de escombros por explosiones y otros. Sobrepasar este límite causará que el exceso se desvanezca."; TOOLTIP_SWWM_ENEMYDROPS = "Al activar, los enemigos soltarán armas y munición como harían en vanilla. Ésto no se recomienda ya que rompe fácilmente la progresión."; +TOOLTIP_SWWM_SHOTGIB = "Hay gente a la que no le gusta esto por alguna razón, así que aquí está como opción."; // knowledge base SWWM_COMINGSOON = "(próximamente)"; SWWM_MISSTAB = "Misión"; diff --git a/language.version b/language.version index bb64f77a3..e1bd07311 100644 --- a/language.version +++ b/language.version @@ -1,2 +1,2 @@ [default] -SWWM_MODVER="\chSWWM \cwGZ\c- r542 (Sun 13 Sep 10:14:01 CEST 2020)"; +SWWM_MODVER="\chSWWM \cwGZ\c- r543 (Sun 13 Sep 14:39:24 CEST 2020)"; diff --git a/menudef.txt b/menudef.txt index d4c42a71e..5be3d1577 100644 --- a/menudef.txt +++ b/menudef.txt @@ -71,6 +71,9 @@ OptionMenu "SWWMOptionMenu" StaticText " " StaticText "$SWWM_FTITLE", "Gold" Option "$SWWM_FUNTAGS", "swwm_funtags", "YesNo" + //Option "$SWWM_OMNIBUST", "swwm_omnibust", "YesNo" + //Option "$SWWM_SUPERFUEL", "swwm_superfuel", "YesNo" + //Option "$SWWM_PARTYTIME", "swwm_partytime", "YesNo" StaticText " " StaticText "$SWWM_BTITLE", "Gold" Option "$SWWM_ENEMYDROPS", "swwm_enemydrops", "YesNo" @@ -84,6 +87,7 @@ OptionMenu "SWWMOptionMenu" Option "$SWWM_NOTRACK", "swwm_notrack", "YesNo" Option "$SWWM_YNYKRONALERT", "swwm_ynykronalert", "YesNo" Option "$SWWM_SIMPLEFOG", "swwm_simplefog", "YesNo" + Option "$SWWM_SHOTGIB", "swwm_shotgib", "YesNo" StaticText " " StaticText "$SWWM_ITITLE", "Gold" Option "$SWWM_ARMORUSE", "swwm_autousearmor", "YesNo" diff --git a/zscript/swwm_blazeit.zsc b/zscript/swwm_blazeit.zsc index d9ea5a79d..67f4d3ada 100644 --- a/zscript/swwm_blazeit.zsc +++ b/zscript/swwm_blazeit.zsc @@ -656,6 +656,7 @@ Mixin Class HellblazerGrenade // find closest 3d floor for its normal for ( int i=0; i= radius*steps) || (abs(vel.y) >= radius*steps) || (abs(vel.z) >= height*steps) ) steps++; @@ -211,6 +211,7 @@ Class mkBloodDrop : Actor F3DFloor ff; for ( int i=0; i= radius*steps) || (abs(vel.y) >= radius*steps) || (abs(vel.z) >= height*steps) ) + steps++; + bool domove = (vel!=(0,0,0)); + if ( domove ) + { + Vector3 steppy = vel/steps; + int changexy = steppy.X||steppy.Y; + bool readjust = false; + for ( int i=0; i ceilingz ) + { + // hit the ceiling, slide along it + SetZ(ceilingz-height); + F3DFloor ff; + for ( int i=0; i 0) && !bAMBUSH ) { let b = Spawn("SWWMBubble",pos); b.scale *= abs(scale.x); b.vel = vel; Destroy(); + return; + } + if ( tics > 0 ) tics--; + while ( !tics ) + { + if ( !SetState(CurState.NextState) ) + return; } } @@ -142,7 +211,7 @@ Class SWWMSmoke : Actor } } -// ultra-lightweight non-interacting smoke, used by fire trails +// strictly non-interacting smoke, much lighter tick, used for heavier effects Class SWWMHalfSmoke : Actor { Default @@ -175,6 +244,7 @@ Class SWWMHalfSmoke : Actor } override void Tick() { + prev = pos; // for interpolation if ( isFrozen() ) return; vel *= 0.96; vel.z += 0.01; @@ -208,7 +278,6 @@ Class SWWMSmallSmoke : SWWMHalfSmoke { override void PostBeginPlay() { - Actor.PostBeginPlay(); double ang, pt; scale *= FRandom[Puff](0.1,0.3); alpha *= FRandom[Puff](0.5,1.5); @@ -228,13 +297,13 @@ Class SWWMSmallSmoke : SWWMHalfSmoke } } -Class SWWMViewSmoke : SWWMSmoke +// visual smoke, very strictly non-interacting, updates even when game is frozen +Class SWWMViewSmoke : SWWMHalfSmoke { Vector3 ofs, vvel; override void PostBeginPlay() { - Actor.PostBeginPlay(); double ang, pt; scale *= FRandom[Puff](0.1,0.3); alpha *= FRandom[Puff](0.5,1.5); @@ -245,7 +314,7 @@ Class SWWMViewSmoke : SWWMSmoke override void Tick() { - Actor.Tick(); + prev = pos; // for interpolation if ( !target || !target.player ) { Destroy(); @@ -255,12 +324,18 @@ Class SWWMViewSmoke : SWWMSmoke [x, y, z] = swwm_CoordUtil.GetAxes(target.pitch,target.angle,target.roll); Vector3 origin = level.Vec3Offset(target.Vec2OffsetZ(0,0,target.player.viewz),x*ofs.x+y*ofs.y+z*ofs.z); SetOrigin(origin,true); + UpdateWaterLevel(); bInvisible = (players[consoleplayer].camera != target); - if ( isFrozen() ) return; ofs += vvel; vvel *= 0.96; vvel.z += 0.01; if ( (waterlevel > 0) && !bAMBUSH ) Destroy(); + if ( tics > 0 ) tics--; + while ( !tics ) + { + if ( !SetState(CurState.NextState) ) + return; + } } } @@ -276,11 +351,12 @@ Class SWWMBubble : Actor +DONTSPLASH; +FORCEXYBILLBOARD; +NOTELEPORT; + +THRUACTORS; + +NOINTERACTION; Scale 0.5; } override void PostBeginPlay() { - Super.PostBeginPlay(); double ang, pt; scale *= FRandom[Puff](0.5,1.5); ang = FRandom[Puff](0,360); @@ -291,11 +367,85 @@ Class SWWMBubble : Actor } override void Tick() { - Super.Tick(); + prev = pos; if ( isFrozen() ) return; vel *= 0.96; vel.z += 0.05; + FCheckPosition tm; + // movement subdivision (these damn things are tiny) + int steps = 8; + while ( (abs(vel.x) >= radius*steps) || (abs(vel.y) >= radius*steps) || (abs(vel.z) >= height*steps) ) + steps++; + bool domove = (vel!=(0,0,0)); + if ( domove ) + { + Vector3 steppy = vel/steps; + int changexy = steppy.X||steppy.Y; + bool readjust = false; + for ( int i=0; i ceilingz ) + { + // hit the ceiling, slide along it + SetZ(ceilingz-height); + F3DFloor ff; + for ( int i=0; i 0 ) tics--; + while ( !tics ) + { + if ( !SetState(CurState.NextState) ) + return; + } } States { @@ -307,6 +457,10 @@ Class SWWMBubble : Actor Class SWWMSpark : Actor { + bool dead; + Sector tracksector; + int trackplane; + Default { RenderStyle "Add"; @@ -314,25 +468,143 @@ Class SWWMSpark : Actor Height 2; +NOBLOCKMAP; +FORCEXYBILLBOARD; - +MISSILE; - +DROPOFF; +THRUACTORS; +NOTELEPORT; +DONTSPLASH; - BounceType "Doom"; - BounceFactor 0.4; + +NOINTERACTION; Gravity 0.2; Scale 0.05; } override void Tick() { - Super.Tick(); + prev = pos; + if ( isFrozen() ) return; + if ( dead ) + { + // do nothing but follow floor movement + if ( tracksector ) + { + double trackz; + if ( trackplane ) trackz = tracksector.ceilingplane.ZAtPoint(pos.xy); + else trackz = tracksector.floorplane.ZAtPoint(pos.xy); + if ( trackz != pos.z ) + { + SetZ(trackz); + UpdateWaterLevel(false); + } + } + } + else + { + vel.z -= GetGravity(); + FCheckPosition tm; + // movement subdivision (these damn things are tiny) + int steps = 8; + while ( (abs(vel.x) >= radius*steps) || (abs(vel.y) >= radius*steps) || (abs(vel.z) >= height*steps) ) + steps++; + bool domove = (vel!=(0,0,0)); + if ( domove ) + { + Vector3 steppy = vel/steps; + int changexy = steppy.X||steppy.Y; + bool readjust = false; + for ( int i=0; i ceilingz ) + { + // hit the ceiling, bounce on it + SetZ(ceilingz-height); + F3DFloor ff; + for ( int i=0; i 0 ) { let b = Spawn("SWWMBubble",pos); b.vel = vel; b.scale *= 0.3; Destroy(); + return; + } + if ( tics > 0 ) tics--; + while ( !tics ) + { + if ( !SetState(CurState.NextState) ) + return; } } States @@ -351,15 +623,15 @@ Class SWWMChip : Actor SWWMChip prevchip, nextchip; bool killme; double rollvel; + bool dead; + Sector tracksector; + int trackplane; Default { Radius 2; Height 2; +NOBLOCKMAP; - +MISSILE; - +DROPOFF; - +MOVEWITHSECTOR; +THRUACTORS; +NOTELEPORT; +DONTSPLASH; @@ -367,14 +639,12 @@ Class SWWMChip : Actor +ROLLSPRITE; +ROLLCENTER; +FORCEXYBILLBOARD; - BounceType "Doom"; - BounceFactor 0.3; + +NOINTERACTION; Gravity 0.35; Scale 0.2; } override void PostBeginPlay() { - Super.PostBeginPlay(); rollvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); frame = Random[Junk](0,5); scale *= Frandom[Junk](0.8,1.2); @@ -387,14 +657,135 @@ Class SWWMChip : Actor } override void Tick() { - Super.Tick(); + prev = pos; // for interpolation if ( isFrozen() ) return; + if ( dead ) + { + // do nothing but follow floor movement + if ( tracksector ) + { + double trackz; + if ( trackplane ) trackz = tracksector.ceilingplane.ZAtPoint(pos.xy); + else trackz = tracksector.floorplane.ZAtPoint(pos.xy); + if ( trackz != pos.z ) + { + SetZ(trackz); + UpdateWaterLevel(false); + } + } + } + else + { + vel.z -= GetGravity(); + FCheckPosition tm; + // movement subdivision (these damn things are tiny) + int steps = 8; + while ( (abs(vel.x) >= radius*steps) || (abs(vel.y) >= radius*steps) || (abs(vel.z) >= height*steps) ) + steps++; + bool domove = (vel!=(0,0,0)); + if ( domove ) + { + Vector3 steppy = vel/steps; + int changexy = steppy.X||steppy.Y; + bool readjust = false; + for ( int i=0; i ceilingz ) + { + // hit the ceiling, bounce on it + SetZ(ceilingz-height); + F3DFloor ff; + for ( int i=0; i 0 ) { - vel.xy *= .98; + vel *= .98; rollvel *= .98; } + if ( !CheckNoDelay() || (tics == -1) ) return; + if ( tics > 0 ) tics--; + while ( !tics ) + { + if ( !SetState(CurState.NextState) ) + return; + } } States { @@ -619,7 +1010,7 @@ Class SmolInvisibleSplasher : InvisibleSplasher { Default { - Mass 25; + Mass 10; } } @@ -690,7 +1081,7 @@ Class SWWMBulletTrail : LineTracer } } -// Elastic recoil from DT +// Elastic recoil from DT (unused) Enum ESwingMode { SWING_Straight, // constant increment diff --git a/zscript/swwm_danmaku.zsc b/zscript/swwm_danmaku.zsc index b72e89c8f..7da2ea5f7 100644 --- a/zscript/swwm_danmaku.zsc +++ b/zscript/swwm_danmaku.zsc @@ -152,6 +152,7 @@ Class EvisceratorChunk : Actor // find closest 3d floor for its normal for ( int i=0; i level.maptime ) updated = level.maptime; + lasthealth = 0; + prevpos = pos; // prevent stuttering + intp.Update(lasthealth); + if ( level.maptime > updated+35 ) Destroy(); + return; + } + // only update height/position while alive + bool heightchanged = false; + if ( height != mytarget.height ) heightchanged = true; + height = mytarget.height; + if ( mytarget && (heightchanged || (mytarget.pos != oldpos) || (mytarget.prev != oldprev)) ) { oldpos = mytarget.pos; oldprev = mytarget.prev; pos = level.Vec3Offset(mytarget.pos,(0,0,height)); prevpos = level.Vec3Offset(mytarget.prev,(0,0,height)); } - if ( !mytarget || (mytarget.Health <= 0) ) - { - // we're done - if ( updated > level.maptime ) updated = level.maptime; - lasthealth = 0; - intp.Update(lasthealth); - if ( level.maptime > updated+35 ) Destroy(); - return; - } - // only update height while alive - height = mytarget.height; tcnt++; if ( (tcnt == 1) && !mytarget.player ) { diff --git a/zscript/swwm_utility.zsc b/zscript/swwm_utility.zsc index fe6d78b28..2f00ce09a 100644 --- a/zscript/swwm_utility.zsc +++ b/zscript/swwm_utility.zsc @@ -224,6 +224,7 @@ Class SWWMUtility F3DFloor ff; for ( int i=0; i