From aad61ba649b32fcee6c8ddb026103f2336ee42a3 Mon Sep 17 00:00:00 2001 From: Marisa Kirisame Date: Mon, 16 Mar 2020 21:24:42 +0100 Subject: [PATCH] Implemented trading tab. Added hitscan parrying (not that it's very useful). Various fixes to some timing stuff. Added "F to pay respects" when in death screen. Added money cheat, figure out the key combination yourself. Changed score interpolator so it goes faster with huge score gains. --- TODO.md | 2 - language.def | 4 +- language.es | 4 +- modeldef.misc | 14 ++ zscript/swwm_common.zsc | 130 +++++++++++++-- zscript/swwm_hud.zsc | 14 +- zscript/swwm_inventory.zsc | 40 ++++- zscript/swwm_menu.zsc | 322 +++++++++++++++++++++++++++++++++++-- 8 files changed, 491 insertions(+), 39 deletions(-) create mode 100644 modeldef.misc diff --git a/TODO.md b/TODO.md index cad6e686b..68d842ea0 100644 --- a/TODO.md +++ b/TODO.md @@ -1,12 +1,10 @@ Fundamental things: - - Eviscerator - Hellblazer - Sparkster - Silver Bullet - Ynykron - Wallbuster - Custom intermission - - Trading tab Extra things: - Art for custom intermission diff --git a/language.def b/language.def index cb2d661db..6cc82ae56 100644 --- a/language.def +++ b/language.def @@ -143,7 +143,6 @@ SWWM_STOREMUNS = "You don't have enough money"; SWWM_NOSTORE = "(there is nothing left to buy)"; SWWM_TRADETAB = "Trading"; SWWM_TRADEFULL = "They can't hold more of that"; -SWWM_TRADEHIST = "History"; SWWM_TRADETO = "Sent to"; SWWM_TRADEFROM = "Received from"; SWWM_NOTRADE = "(there is no one to trade with)"; @@ -207,7 +206,8 @@ SWWM_HELPTXT = "\cx---------------------------\c-\n" "\n" "\cfUp/Down:\c- Navigate\n" -"\cfEnter:\c- Select option\n" +"\cfEnter:\c- Select recipient\n" +"\cfBackspace:\c- History\n" "\n" "\cxTrading Tab - Controls (Trade)\c-\n" "\cx----------------------------\c-\n" diff --git a/language.es b/language.es index 2bc5f797c..e3a1d397c 100644 --- a/language.es +++ b/language.es @@ -139,7 +139,6 @@ SWWM_STOREMUNS = "No tienes suficiente dinero"; SWWM_NOSTORE = "(no queda nada que comprar)"; SWWM_TRADETAB = "Intercambio"; SWWM_TRADEFULL = "No puede llevar más de eso"; -SWWM_TRADEHIST = "Historial"; SWWM_TRADETO = "Enviado a"; SWWM_TRADEFROM = "Recibido de"; SWWM_NOTRADE = "(no hay nadie con quien intercambiar)"; @@ -203,7 +202,8 @@ SWWM_HELPTXT = "\cx---------------------------\c-\n" "\n" "\cfArriba/Abajo:\c- Navegar\n" -"\cfEnter:\c- Elegir opción\n" +"\cfEnter:\c- Elegir destinatario\n" +"\cfRetroceso:\c- Historial\n" "\n" "\cxPestaña de Intercambio - Controles (Intercambio)\c-\n" "\cx----------------------------\c-\n" diff --git a/modeldef.misc b/modeldef.misc new file mode 100644 index 000000000..006277688 --- /dev/null +++ b/modeldef.misc @@ -0,0 +1,14 @@ +Model "ReflectedBullet" +{ + Path "models" + + Model 0 "Boolet_d.3d" + Skin 0 "Boolet.png" + Scale 0.003 0.003 0.003 + USEACTORPITCH + USEACTORROLL + AngleOffset -90 + ZOffset 1 + + FrameIndex XZW1 A 0 0 +} diff --git a/zscript/swwm_common.zsc b/zscript/swwm_common.zsc index ee45e0f8c..7ecf17086 100644 --- a/zscript/swwm_common.zsc +++ b/zscript/swwm_common.zsc @@ -182,8 +182,9 @@ Class SWWMCredits : Thinker // Trading history between players Class SWWMTrade { - int timestamp, type; - String other, what; + int timestamp, type, amt; + String other; + Class what; } Class SWWMTradeHistory : Thinker @@ -191,26 +192,28 @@ Class SWWMTradeHistory : Thinker PlayerInfo myplayer; Array ent; - static void RegisterSend( PlayerInfo p, PlayerInfo other, String what ) + static void RegisterSend( PlayerInfo p, PlayerInfo other, Class what, int amt ) { let th = Find(p); if ( !th ) return; SWWMTrade t = new("SWWMTrade"); - t.timestamp = gametic; + t.timestamp = level.totaltime; t.type = 0; t.other = other.GetUserName(); t.what = what; + t.amt = amt; th.ent.Push(t); } - static void RegisterReceive( PlayerInfo p, PlayerInfo other, String what ) + static void RegisterReceive( PlayerInfo p, PlayerInfo other, Class what, int amt ) { let th = Find(p); if ( !th ) return; SWWMTrade t = new("SWWMTrade"); - t.timestamp = gametic; + t.timestamp = level.totaltime; t.type = 1; t.other = other.GetUserName(); t.what = what; + t.amt = amt; th.ent.Push(t); } @@ -659,6 +662,59 @@ Class SWWMCombatTracker : Thinker } } +// Press F to Pay Respects +Class PayRespects : HUDMessageBase +{ + Vector2 basepos; + int lifespan, initialspan, starttic; + transient Font TewiFont; + double scale; + Vector2 hs, ss; + int seed, seed2; + + static PayRespects PressF() + { + let f = new("PayRespects"); + f.basepos = (FRandom[FInTheChat](0.,1.),FRandom[FInTheChat](1.02,1.05)); + f.scale = FRandom[FInTheChat](.5,2.); + f.lifespan = f.initialspan = Random[FInTheChat](20,80); + f.starttic = level.maptime; + f.seed = Random[FInTheChat](); + f.seed2 = Random[FInTheChat](); + f.ScreenSizeChanged(); + return f; + } + + override bool Tick() + { + lifespan--; + return (lifespan<=0); + } + + override void ScreenSizeChanged() + { + hs = StatusBar.GetHUDScale()*scale; + ss = (Screen.GetWidth()/hs.x,Screen.GetHeight()/hs.y); + } + + override void Draw( int bottom, int visibility ) + { + Vector2 realpos = (basepos.x*ss.x,basepos.y*ss.y); + if ( !TewiFont ) TewiFont = Font.GetFont('TewiShaded'); + Vector2 fo = (TewiFont.StringWidth("F")/2.,-TewiFont.GetHeight()); + // F rise up + int initspd = (128-seed); + if ( (initspd >= 0) && (initspd < 32) ) initspd = 32; + if ( (initspd < 0) && (initspd > -32) ) initspd = -32; + int boostup = 32+(seed2/4); + double fractic = SWWMStatusBar(statusbar)?SWWMStatusBar(statusbar).fractic:0; + fo.x += (.15*initspd)*((initialspan-(lifespan-fractic))**.6); + fo.y += ((initialspan-(lifespan-fractic))**1.6)-boostup*sin((90./initialspan)*(level.maptime+fractic-starttic)); + double alph = clamp((lifespan+fractic)/double(initialspan),0.,1.); + Screen.DrawText(TewiFont,Font.CR_GREEN,realpos.x-fo.x,realpos.y-fo.y,"F",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph); + } +} + // One-liners Class SWWMOneLiner : HUDMessageBase { @@ -1393,6 +1449,8 @@ Class SWWMHandler : EventHandler int multilevel[MAXPLAYERS]; int lastitemcount[MAXPLAYERS]; bool allkills, allitems, allsecrets; + // for money cheat + transient ui int kcode; // heal/armor flashes need to be handled here so they don't stack transient int hflash[MAXPLAYERS], aflash[MAXPLAYERS]; @@ -2003,6 +2061,41 @@ Class SWWMHandler : EventHandler } } + override bool InputProcess( InputEvent e ) + { + if ( e.Type == InputEvent.TYPE_KeyDown ) // assuming that's the F key on all keyboards (hopefully) + { + static const int lods[] = {38,24,32,31,24,33,18,50,24,49,18}; + // what's that spell? + // loadsamoney! ... probably + if ( e.KeyScan == lods[kcode] ) + { + kcode++; + if ( kcode >= 11 ) + { + SendNetworkEvent("swwmmoneycheat",consoleplayer); + kcode = 0; + } + } + else kcode = 0; + if ( e.KeyScan == 33 ) + { + let demo = Demolitionist(players[consoleplayer].mo); + if ( demo && (demo.Health <= 0) && (demo.deadtimer > 40) ) + { + // pay respects + int numf = Random[FInTheChat](1,6); + for ( int i=0; i ramt ) amt = ramt; - if ( players[e.Args[1]].mo.GiveInventory(item,amt) ) + Inventory ritm = players[e.Args[1]].mo.FindInventory(item); + if ( ritm ) + { + int maxgive = ritm.MaxAmount-ritm.Amount; + if ( amt > maxgive ) amt = maxgive; + } + else if ( amt > def.MaxAmount ) amt = def.MaxAmount; + if ( (amt > 0) && players[e.Args[1]].mo.GiveInventory(item,amt) ) { players[e.Args[0]].mo.TakeInventory(item,amt); // add to history - SWWMTradeHistory.RegisterSend(players[e.Args[0]],players[e.Args[1]],def.GetTag()); - SWWMTradeHistory.RegisterReceive(players[e.Args[1]],players[e.Args[0]],def.GetTag()); + SWWMTradeHistory.RegisterSend(players[e.Args[0]],players[e.Args[1]],item,amt); + SWWMTradeHistory.RegisterReceive(players[e.Args[1]],players[e.Args[0]],item,amt); // add messages if ( e.Args[0] == consoleplayer ) Console.Printf(StringTable.Localize("$SWWM_MSGSENT"),amt,def.GetTag(),players[e.Args[1]].GetUserName()); if ( e.Args[1] == consoleplayer ) Console.Printf(StringTable.Localize("$SWWM_MSGRECV"),players[e.Args[0]].GetUserName(),amt,def.GetTag()); @@ -2381,6 +2479,16 @@ Class SWWMHandler : EventHandler if ( dist < 2000 ) Console.Printf("\cx%s\cx: %s\c-",players[e.Args[0]].GetUserName(),StringTable.Localize(e.Name.Mid(19))); } + else if ( e.Name ~== "swwmmoneycheat" ) + { + if ( consoleplayer == e.Args[0] ) + { + Console.Printf("\cfLOADSAMONEY!\c-"); + S_StartSound("menu/buyinv",CHAN_ITEM,CHANF_UI); + S_StartSound("misc/emone",CHAN_VOICE,CHANF_UI); + } + SWWMCredits.Give(players[e.Args[0]],999999999); + } } // stuff for hud diff --git a/zscript/swwm_hud.zsc b/zscript/swwm_hud.zsc index dd509cf7f..61797122e 100644 --- a/zscript/swwm_hud.zsc +++ b/zscript/swwm_hud.zsc @@ -99,7 +99,7 @@ Class SWWMStatusBar : BaseStatusBar let m = new("MsgLine"); m.str = outline.Left(outline.Length()-1); // strip newline m.type = printlevel; - m.tic = level.maptime; + m.tic = level.totaltime; m.rep = 1; // append chat messages to full history if ( (printlevel == PRINT_CHAT) || (printlevel == PRINT_TEAMCHAT) ) @@ -202,14 +202,14 @@ Class SWWMStatusBar : BaseStatusBar // prune old messages for ( int i=0; i PRINT_HIGH) && (level.maptime < (MainQueue[i].tic+35*chatduration.GetInt())) ) continue; + if ( (MainQueue[i].type <= PRINT_HIGH) && (level.totaltime < (MainQueue[i].tic+35*msgduration.GetInt())) ) continue; + else if ( (MainQueue[i].type > PRINT_HIGH) && (level.totaltime < (MainQueue[i].tic+35*chatduration.GetInt())) ) continue; MainQueue.Delete(i); i--; } @@ -363,7 +363,7 @@ Class SWWMStatusBar : BaseStatusBar mMiniwiFont = HUDFont.Create("MiniwiShaded"); mMPlusFont = HUDFont.Create("MPlusShaded"); HealthInter = DynamicValueInterpolator.Create(100,.1,1,100); - ScoreInter = DynamicValueInterpolator.Create(0,.1,1,1000); + ScoreInter = DynamicValueInterpolator.Create(0,.1,1,999999999); FuelInter = DynamicValueInterpolator.Create(120,.5,1,100); DashInter = DynamicValueInterpolator.Create(120,.5,1,40); gl_proj = new("swwmLe__GLScreen"); @@ -755,7 +755,7 @@ Class SWWMStatusBar : BaseStatusBar else if ( MainQueue[i].type == PRINT_TEAMCHAT ) col = teamcol.GetInt(); String cstr = MainQueue[i].str; if ( MainQueue[i].rep > 1 ) cstr.AppendFormat(" (x%d)",MainQueue[i].rep); - int curtime = MainQueue[i].tic-level.maptime; + int curtime = MainQueue[i].tic-level.totaltime; if ( MainQueue[i].type < PRINT_CHAT ) curtime += 35*msgduration.GetInt(); else curtime += 35*chatduration.GetInt(); double alph = clamp(curtime/20.,0.,1.); @@ -780,7 +780,7 @@ Class SWWMStatusBar : BaseStatusBar { String cstr = PickupQueue[i].str; if ( PickupQueue[i].rep > 1 ) cstr.AppendFormat(" (x%d)",PickupQueue[i].rep); - int curtime = (PickupQueue[i].tic+35*pickduration.GetInt())-level.maptime; + int curtime = (PickupQueue[i].tic+35*pickduration.GetInt())-level.totaltime; double alph = clamp(curtime/20.,0.,1.); Font fnt = LangFont(mTewiFont); BrokenLines l = fnt.BreakLines(cstr,int(ss.x*.75)); diff --git a/zscript/swwm_inventory.zsc b/zscript/swwm_inventory.zsc index e6ae19cfb..517ea1e73 100644 --- a/zscript/swwm_inventory.zsc +++ b/zscript/swwm_inventory.zsc @@ -447,6 +447,8 @@ Class BigPunchImpact : Actor } } +Class ReflectedBullet : SWWMCasing {} + Class ParryField : Actor { Default @@ -516,12 +518,40 @@ Class ParryField : Actor { if ( (flags&DMG_INFLICTOR_IS_PUFF) && (source != master) ) { - // TODO deflect hitscan - // three options: - // 1. just block the bullet, making it bounce off - // 2. ricochet in random direction - // 3. ricochet directly back to source + Vector3 vdir = (cos(angle),sin(angle),0); + Vector3 vpos = inflictor.pos; // puff goes here + bool raging = !!master.FindInventory("RagekitPower"); + let i = Spawn(raging?"BigPunchImpact":"PunchImpact",vpos); + i.angle = angle+180; + // deflect hitscan + A_StartSound("misc/ricochet",CHAN_VOICE,CHANF_OVERLAP,.7); A_StartSound("demolitionist/parry",CHAN_VOICE,CHANF_OVERLAP,.4); + // three options: + switch ( Random[Parry](0,7) ) + { + case 0: + case 1: + case 2: + // 1. just block the bullet, making it bounce off + let b = Spawn("ReflectedBullet",vpos); + b.angle = angle; + b.target = self; + b.vel = (-vdir+(FRandom[Parry](-.2,.2),FRandom[Parry](-.2,.2),FRandom[Parry](-.2,.2))).unit()*FRandom[Parry](raging?9:3,raging?20:8); + break; + case 3: + case 4: + case 5: + // 2. ricochet in random direction + Vector3 rdir = (-vdir+(FRandom[Parry](-.4,.4),FRandom[Parry](-.4,.4),FRandom[Parry](-.4,.4))).unit(); + LineAttack(atan2(rdir.y,rdir.x),8000,asin(-rdir.z),raging?(damage*8):damage,mod,inflictor.GetClass(),LAF_ABSPOSITION,null,vpos.z,vpos.x,vpos.y); + break; + case 6: + case 7: + // 3. ricochet directly back to source + Vector3 tdir = source?(level.Vec3Diff(vpos,source.Vec3Offset(0,0,source.height/2))):(-vdir); + LineAttack(atan2(tdir.y,tdir.x),8000,asin(-tdir.z),raging?(damage*8):damage,mod,inflictor.GetClass(),LAF_ABSPOSITION,null,vpos.z,vpos.x,vpos.y); + break; + } } return 0; } diff --git a/zscript/swwm_menu.zsc b/zscript/swwm_menu.zsc index 31b9c49e4..47759a00e 100644 --- a/zscript/swwm_menu.zsc +++ b/zscript/swwm_menu.zsc @@ -36,6 +36,7 @@ Class SWWMKnowledgeBaseMenu : GenericMenu Array storeunits; // trading SWWMTradeHistory tradelib; + Array playerlist; // chat history total line count (includes breaks) int chatlines; // temporary bottom messages, such as "not enough money" @@ -51,6 +52,7 @@ Class SWWMKnowledgeBaseMenu : GenericMenu int lastuseamt; int checkuse; int checkdrop; + int checksend; // seeeeecret int kcode; @@ -91,9 +93,10 @@ Class SWWMKnowledgeBaseMenu : GenericMenu tcol = CVar.GetCVar("msg4color",players[consoleplayer]); lorelib = SWWMLoreLibrary.Find(players[consoleplayer]); oldloresiz = lorelib.ent.Size(); + tradelib = SWWMTradeHistory.Find(players[consoleplayer]); stats = SWWMStats.Find(players[consoleplayer]); tmsg = "$SWWM_MAINCONTROLS"; - tmsgtic = gametic+70; + tmsgtic = gametic+140; } override bool MenuEvent( int mkey, bool fromcontroller ) @@ -150,6 +153,7 @@ Class SWWMKnowledgeBaseMenu : GenericMenu lorelist.Clear(); storelist.Clear(); storeunits.Clear(); + playerlist.Clear(); return true; case MKEY_PAGEDOWN: MenuSound("menu/demotab"); @@ -162,6 +166,7 @@ Class SWWMKnowledgeBaseMenu : GenericMenu lorelist.Clear(); storelist.Clear(); storeunits.Clear(); + playerlist.Clear(); return true; case MKEY_DOWN: if ( curtab == TAB_HELP ) @@ -231,6 +236,34 @@ Class SWWMKnowledgeBaseMenu : GenericMenu if ( sel0 >= storelist.Size() ) sel0 = 0; } + else if ( curtab == TAB_TRADING ) + { + if ( !sub && (playerlist.Size() > 1) ) + { + // scroll through player list + MenuSound("menu/demoscroll"); + sel0++; + if ( sel0 >= playerlist.Size() ) + sel0 = 0; + } + else if ( sub ) + { + if ( (sel0 == -1) && (sel1 < tradelib.ent.Size()-28) ) + { + // scroll through trading history + MenuSound("menu/demoscroll"); + sel1++; + } + else if ( invlist.Size() > 1 ) + { + // scroll through inventory + MenuSound("menu/demoscroll"); + sel1++; + if ( sel1 >= invlist.Size() ) + sel1 = 0; + } + } + } return true; case MKEY_UP: if ( curtab == TAB_HELP ) @@ -286,6 +319,34 @@ Class SWWMKnowledgeBaseMenu : GenericMenu if ( sel0 < 0 ) sel0 = storelist.Size()-1; } + else if ( curtab == TAB_TRADING ) + { + if ( !sub && (playerlist.Size() > 1) ) + { + // scroll through player list + MenuSound("menu/demoscroll"); + sel0--; + if ( sel0 < 0 ) + sel0 = playerlist.Size()-1; + } + else if ( sub ) + { + if ( (sel0 == -1) && (sel1 > 0) ) + { + // scroll through trading history + MenuSound("menu/demoscroll"); + sel1--; + } + else if ( invlist.Size() > 1 ) + { + // scroll through inventory + MenuSound("menu/demoscroll"); + sel1--; + if ( sel1 < 0 ) + sel1 = invlist.Size()-1; + } + } + } return true; case MKEY_RIGHT: if ( ((curtab == TAB_INVENTORY) || (curtab == TAB_KEYS)) && (invlist.Size() > 21) ) @@ -311,6 +372,23 @@ Class SWWMKnowledgeBaseMenu : GenericMenu if ( sel1 > LORE_LORE ) sel1 = LORE_ITEM; } + else if ( curtab == TAB_TRADING ) + { + if ( !sub && (playerlist.Size() > 21) ) // lol is this ever going to happen + { + int oldsel = sel0; + sel0 += 22; + if ( sel0 >= playerlist.Size() ) sel0 = playerlist.Size()-1; + if ( sel0 != oldsel ) MenuSound("menu/demoscroll"); + } + else if ( sub && (sel0 != -1) ) + { + int oldsel = sel1; + sel1 += 22; + if ( sel1 >= invlist.Size() ) sel1 = invlist.Size()-1; + if ( sel1 != oldsel ) MenuSound("menu/demoscroll"); + } + } return true; case MKEY_LEFT: if ( ((curtab == TAB_INVENTORY) || (curtab == TAB_KEYS)) && (invlist.Size() > 21) ) @@ -338,6 +416,25 @@ Class SWWMKnowledgeBaseMenu : GenericMenu if ( sel1 < LORE_ITEM ) sel1 = LORE_LORE; } + else if ( curtab == TAB_TRADING ) + { + if ( !sub && (playerlist.Size() > 21) ) // lol is this ever going to happen + { + if ( sel0-22 >= 0 ) + { + sel0 -= 22; + MenuSound("menu/demoscroll"); + } + } + else if ( sub && (sel0 != -1) ) + { + if ( sel1-22 >= 0 ) + { + sel1 -= 22; + MenuSound("menu/demoscroll"); + } + } + } return true; case MKEY_ENTER: if ( (curtab == TAB_INVENTORY) && (invlist.Size() > 0) && (sel0 < invlist.Size()) ) @@ -348,7 +445,7 @@ Class SWWMKnowledgeBaseMenu : GenericMenu lastuseamt = invlist[sel0].Amount; // don't check weapons (or the lamp) if ( !(invlist[sel0] is 'Weapon') && !(invlist[sel0] is 'SWWMLamp') ) - checkuse = gametic+1; + checkuse = gametic+(multiplayer?5:1); EventHandler.SendNetworkEvent(String.Format("swwmuseitem.%s",invlist[sel0].GetClassName()),consoleplayer); } else if ( (curtab == TAB_STORE) && (storelist.Size() > 0) && (sel0 < storelist.Size()) ) @@ -361,7 +458,7 @@ Class SWWMKnowledgeBaseMenu : GenericMenu { MenuSound("menu/noinvuse"); tmsg = StringTable.Localize("$SWWM_STOREMUNS"); - tmsgtic = gametic+35; + tmsgtic = gametic+70; return true; } let cur = players[consoleplayer].mo.FindInventory(storelist[sel0]); @@ -380,10 +477,11 @@ Class SWWMKnowledgeBaseMenu : GenericMenu { MenuSound("menu/noinvuse"); tmsg = StringTable.Localize("$SWWM_STOREFULL"); - tmsgtic = gametic+35; + tmsgtic = gametic+70; return true; } EventHandler.SendNetworkEvent(String.Format("swwmstoregive.%s",storelist[sel0].GetClassName()),consoleplayer,price,amt); + if ( (camt+amt) >= max ) sel0 = max(0,sel0-1); MenuSound("menu/buyinv"); } else if ( curtab == TAB_LIBRARY ) @@ -403,13 +501,31 @@ Class SWWMKnowledgeBaseMenu : GenericMenu sel2 = 0; } } + else if ( curtab == TAB_TRADING ) + { + if ( !sub && (playerlist.Size() > 0) ) + { + // pick a player + MenuSound("menu/demosel"); + sub = true; + sel1 = 0; + } + else if ( sub && (sel0 != -1) && (invlist.Size() > 0) ) + { + // trade item + lastuse = invlist[sel1].GetClass(); + lastuseamt = invlist[sel1].Amount; + checksend = gametic+(multiplayer?5:1); + EventHandler.SendNetworkEvent(String.Format("swwmtrade.%s",invlist[sel1].GetClassName()),consoleplayer,playerlist[sel0]); + } + } return true; case MKEY_CLEAR: if ( (curtab == TAB_INVENTORY) && (invlist.Size() > 0) ) { lastuse = invlist[sel0].GetClass(); lastuseamt = invlist[sel0].Amount; - checkdrop = gametic+1; + checkdrop = gametic+(multiplayer?5:1); EventHandler.SendNetworkEvent(String.Format("swwmdropitem.%s",invlist[sel0].GetClassName()),consoleplayer); } else if ( (curtab == TAB_LIBRARY) && sub ) @@ -417,6 +533,24 @@ Class SWWMKnowledgeBaseMenu : GenericMenu MenuSound("menu/democlose"); sub = false; } + else if ( curtab == TAB_TRADING ) + { + if ( !sub ) + { + // open trading history + MenuSound("menu/demosel"); + sub = true; + sel0 = -1; + sel1 = 0; + } + else if ( sub ) + { + // go back + MenuSound("menu/democlose"); + sub = false; + sel0 = 0; + } + } return true; } return Super.MenuEvent(mkey,fromcontroller); @@ -647,10 +781,59 @@ Class SWWMKnowledgeBaseMenu : GenericMenu if ( lorelib.ent.Size() != oldloresiz ) { tmsg = StringTable.Localize("$SWWM_NEWLORE"); - tmsgtic = gametic+35; + tmsgtic = gametic+70; } oldloresiz = lorelib.ent.Size(); } + else if ( curtab == TAB_TRADING ) + { + // build player list + playerlist.Clear(); + for ( int i=0; i tag2 ) continue; + greater = true; + invlist.Insert(i,inv); + break; + } + if ( greater ) continue; + invlist.Push(inv); + } + // re-sort by category + for ( int i=0; i 0) && CmpInventory(invlist[k-1],invlist[k]) ) + { + Inventory tmp = invlist[k]; + invlist[k] = invlist[k-1]; + invlist[k-1] = tmp; + k--; + } + j++; + } + } + } + } // check if use succeeded if ( checkuse && (gametic > checkuse) ) { @@ -659,7 +842,7 @@ Class SWWMKnowledgeBaseMenu : GenericMenu if ( amt == lastuseamt ) { tmsg = StringTable.Localize("$SWWM_INVFAIL"); - tmsgtic = gametic+35; + tmsgtic = gametic+70; MenuSound("menu/noinvuse"); } else @@ -677,12 +860,30 @@ Class SWWMKnowledgeBaseMenu : GenericMenu if ( amt == lastuseamt ) { tmsg = StringTable.Localize("$SWWM_INVNDROP"); - tmsgtic = gametic+35; + tmsgtic = gametic+70; MenuSound("menu/noinvuse"); } else if ( (amt == 0) && (sel0 >= invlist.Size()) ) sel0 = max(0,sel0-1); } + // check if trade succeeded + if ( checksend && (gametic > checksend) ) + { + checksend = 0; + int amt = players[consoleplayer].mo.CountInv(lastuse); + if ( amt == lastuseamt ) + { + tmsg = StringTable.Localize("$SWWM_TRADEFULL"); + tmsgtic = gametic+70; + MenuSound("menu/noinvuse"); + } + else + { + MenuSound("menu/demosel"); + if ( (amt == 0) && (sel1 >= invlist.Size()) ) + sel1 = max(0,sel1-1); + } + } } override bool OnUiEvent( UIEvent ev ) @@ -1117,8 +1318,109 @@ Class SWWMKnowledgeBaseMenu : GenericMenu } else if ( curtab == TAB_TRADING ) { - str = StringTable.Localize("$SWWM_COMINGSOON"); - Screen.DrawText(fnt,Font.CR_FIRE,(ss.x-fnt.StringWidth(str))/2.,(ss.y-fnt.GetHeight())/2.,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + if ( !sub ) + { + xx = 9; + yy = 23; + int longest = 0, len; + int ofs = int(floor(max(0,sel0-44)/22.)*22); + int cols = 1; + for ( int i=ofs; i longest ) longest = len; + Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(i!=sel0)?Color(128,0,0,0):Color(0,0,0,0)); + yy += 16; + if ( yy >= 370 ) + { + xx += longest+24; + yy = 23; + longest = 0; + cols++; + if ( cols > 3 ) break; + } + } + if ( playerlist.Size() > 65 ) + { + // draw scrollbar + int szr = (playerlist.Size()/22)-2; + xx = floor((ofs/22)*(630./szr))+2; + Screen.DrawText(TewiFont,Font.CR_FIRE,origin.x+xx,origin.y+373,"▬",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + Screen.DrawTexture(WindowSeparatorH,false,origin.x,origin.y+377,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + } + } + else if ( sub ) + { + if ( sel0 == -1 ) + { + xx = 6; + yy = 17; + int ofs = clamp(sel1,0,max(0,tradelib.ent.Size()-28)); + for ( int i=ofs; i= 370 ) break; + let ent = tradelib.ent[tradelib.ent.Size()-(i+1)]; + int thour = (ent.timestamp/(3600*Thinker.TICRATE)); + int tmin = (ent.timestamp/(60*Thinker.TICRATE))%60; + int tsec = (ent.timestamp/Thinker.TICRATE)%60; + str = String.Format("\cu[\cc%02d\cu:\cc%02d\cu:\cc%02d\cu]\c- ",thour,tmin,tsec); + str.AppendFormat("\cd%s %s\cd: \cj%dx \cf%s\c-",ent.type?StringTable.Localize("$SWWM_TRADEFROM"):StringTable.Localize("$SWWM_TRADETO"),ent.other,ent.amt,GetDefaultByType(ent.what).GetTag()); + Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + yy += 13; + } + // scrollbar + if ( tradelib.ent.Size() > 28 ) + { + Screen.DrawTexture(WindowSeparator,false,origin.x+631,origin.y+14,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + xx = 634; + int szr = tradelib.ent.Size()-28; + yy = floor(ofs*(353./szr))+17; + Screen.DrawText(TewiFont,Font.CR_FIRE,origin.x+xx,origin.y+yy,"▮",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + } + } + else + { + xx = 9; + yy = 23; + int longest = 0, len; + int ofs = int(floor(max(0,sel1-44)/22.)*22); + int cols = 1; + for ( int i=ofs; i 1) || (!(invlist[i] is 'PuzzleItem') && (invlist[i].MaxAmount > 1)) ) str = String.Format("%dx %s",invlist[i].Amount,invlist[i].GetTag()); + else str = invlist[i].GetTag(); + len = fnt.StringWidth(str); + if ( len > longest ) longest = len; + int clscol = Font.CR_WHITE; + if ( invlist[i] is 'Weapon' ) clscol = Font.CR_GOLD; + else if ( (invlist[i] is 'Ammo') || (invlist[i] is 'BackpackItem') || (invlist[i] is 'HammerspaceEmbiggener') ) clscol = Font.CR_BROWN; + else if ( (invlist[i] is 'PowerupGiver') || (invlist[i] is 'AmmoFabricator') || invlist[i].bBIGPOWERUP ) clscol = Font.CR_PURPLE; + else if ( (invlist[i] is 'Health') || (invlist[i] is 'HealthPickup') || (invlist[i] is 'SWWMHealth') ) clscol = Font.CR_RED; + else if ( (invlist[i] is 'Armor') || (invlist[i] is 'SWWMSpareArmor') ) clscol = Font.CR_GREEN; + else if ( invlist[i] is 'PuzzleItem' ) clscol = Font.CR_LIGHTBLUE; + Screen.DrawText(fnt,clscol,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(i!=sel1)?Color(128,0,0,0):Color(0,0,0,0)); + yy += 16; + if ( yy >= 370 ) + { + xx += longest+24; + yy = 23; + longest = 0; + cols++; + if ( cols > 3 ) break; + } + } + if ( invlist.Size() > 65 ) + { + // draw scrollbar + int szr = (invlist.Size()/22)-2; + xx = floor((ofs/22)*(630./szr))+2; + Screen.DrawText(TewiFont,Font.CR_FIRE,origin.x+xx,origin.y+373,"▬",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + Screen.DrawTexture(WindowSeparatorH,false,origin.x,origin.y+377,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + } + } + } } else if ( curtab == TAB_CHAT ) {