From 1ca0b9e0e94b266f419907674759ce58feeb7dfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Fri, 16 May 2014 23:32:21 +0200 Subject: [PATCH] World gen rewrite, big cleanup, new items & Chest tile. --- res/img/items.png | Bin 2328 -> 2468 bytes res/img/items.xcf | Bin 5057 -> 5555 bytes res/img/tiles.png | Bin 9092 -> 9764 bytes res/img/tiles.xcf | Bin 23771 -> 21445 bytes src/mightypork/gamecore/app/BaseApp.java | 4 +- .../gamecore/eventbus/EventBus.java | 13 +- src/mightypork/gamecore/util/Utils.java | 31 ++ .../util/error/CorruptDataException.java | 37 ++ .../util/error/CorruptedDataException.java | 37 -- .../util/error/IllegalValueException.java | 12 +- .../util/error/KeyAlreadyExistsException.java | 5 + .../gamecore/util/ion/IonInput.java | 10 +- src/mightypork/gamecore/util/math/Calc.java | 23 + src/mightypork/gamecore/util/math/Range.java | 31 +- .../gamecore/util/math/algo/Coord.java | 2 +- .../util/math/algo/{Step.java => Move.java} | 20 +- .../gamecore/util/math/algo/Moves.java | 95 ++++ .../gamecore/util/math/algo/Sides.java | 79 ---- .../util/math/algo/floodfill/FloodFill.java | 8 +- .../math/algo/pathfinding/PathFinder.java | 16 +- .../algo/pathfinding/PathFinderProxy.java | 6 +- .../color/pal/{COMMODORE.java => CMDR.java} | 2 +- .../gamecore/util/math/color/pal/RGB.java | 1 + .../gamecore/util/objects/ObjectUtils.java | 50 --- .../util/strings/AlphanumComparator.java | 119 +++++ src/mightypork/rogue/GameLoop.java | 2 +- src/mightypork/rogue/Res.java | 17 +- .../rogue/screens/game/HudLayer.java | 22 +- .../rogue/screens/game/IngameNav.java | 2 +- .../rogue/screens/game/InvSlot.java | 4 +- .../{InvLayer.java => InventoryLayer.java} | 16 +- .../rogue/screens/game/NavButton.java | 6 +- .../rogue/screens/game/ScreenGame.java | 34 +- .../screens/game/WorldConsoleRenderer.java | 2 +- .../rogue/screens/menu/ScreenMainMenu.java | 2 +- .../rogue/screens/select_world/WorldSlot.java | 4 +- src/mightypork/rogue/world/Inventory.java | 16 +- src/mightypork/rogue/world/PlayerControl.java | 18 +- src/mightypork/rogue/world/PlayerFacade.java | 30 +- src/mightypork/rogue/world/WorldConsole.java | 39 +- .../rogue/world/entity/EntityPathFinder.java | 10 +- .../rogue/world/entity/impl/BossRatAi.java | 6 +- .../rogue/world/entity/impl/MonsterAi.java | 10 +- .../entity/modules/EntityModulePosition.java | 20 +- .../rogue/world/entity/modules/EntityPos.java | 4 +- .../entity/render/EntityRendererMobLR.java | 2 +- .../rogue/world/gen/LevelBuilder.java | 253 +++++++++++ .../rogue/world/gen/LevelGenerator.java | 124 ------ src/mightypork/rogue/world/gen/MapTheme.java | 3 + .../rogue/world/gen/RoomBuilder.java | 2 +- .../gen/{RoomDesc.java => RoomEntry.java} | 6 +- src/mightypork/rogue/world/gen/Rooms.java | 28 ++ .../rogue/world/gen/ScratchMap.java | 414 +++++++++++------- .../rogue/world/gen/WorldCreator.java | 125 +++++- .../world/gen/rooms/AbstractRectRoom.java | 10 +- .../rogue/world/gen/rooms/BossRoom.java | 38 +- .../rogue/world/gen/rooms/DeadEndRoom.java | 6 +- ...eartPieceRoom.java => ItemShrineRoom.java} | 25 +- .../rogue/world/gen/rooms/Rooms.java | 16 - .../rogue/world/gen/rooms/StorageRoom.java | 50 +++ .../world/gen/rooms/TreasureChestRoom.java | 33 ++ .../rogue/world/gen/rooms/TreasureRoom.java | 38 -- .../rogue/world/gen/themes/ThemeBrick.java | 7 + .../world/gui/interaction/MIPKeyboard.java | 8 +- .../rogue/world/gui/interaction/MIPMouse.java | 10 +- src/mightypork/rogue/world/item/Items.java | 12 +- .../item/impl/active/ItemHeartPiece.java | 2 +- .../world/item/impl/food/ItemCheese.java | 2 +- .../rogue/world/item/impl/food/ItemMeat.java | 2 +- .../world/item/impl/food/ItemSandwich.java | 2 +- .../weapons/{ItemHammer.java => ItemAxe.java} | 10 +- .../world/item/impl/weapons/ItemBone.java | 2 +- .../world/item/impl/weapons/ItemClub.java | 2 +- .../world/item/impl/weapons/ItemKnife.java | 45 ++ .../weapons/{ItemStone.java => ItemRock.java} | 6 +- .../world/item/impl/weapons/ItemSword.java | 4 +- .../world/item/impl/weapons/ItemTwig.java | 45 ++ src/mightypork/rogue/world/level/Level.java | 8 +- .../world/level/render/TileRenderContext.java | 4 +- src/mightypork/rogue/world/tile/Tile.java | 2 + .../rogue/world/tile/TileColors.java | 22 + .../rogue/world/tile/TileRenderer.java | 83 ++-- src/mightypork/rogue/world/tile/TileType.java | 18 +- src/mightypork/rogue/world/tile/Tiles.java | 1 + .../rogue/world/tile/impl/TileBaseChest.java | 101 +++++ .../world/tile/impl/TileBaseEntrance.java | 9 + .../rogue/world/tile/impl/TileBaseExit.java | 9 + .../world/tile/impl/TileBaseSecretDoor.java | 10 +- .../rogue/world/tile/impl/TileWithItems.java | 10 +- .../world/tile/impl/brick/TileBrickChest.java | 25 ++ .../world/tile/impl/brick/TileBrickDoor.java | 6 +- .../tile/impl/brick/TileBrickEntrance.java | 2 +- .../world/tile/impl/brick/TileBrickExit.java | 2 +- .../world/tile/impl/brick/TileBrickFloor.java | 2 +- .../tile/impl/brick/TileBrickPassage.java | 2 +- .../tile/impl/brick/TileBrickSecretDoor.java | 6 +- .../world/tile/impl/brick/TileBrickWall.java | 2 +- .../world/tile/render/ChestRenderer.java | 50 +++ 98 files changed, 1769 insertions(+), 802 deletions(-) create mode 100644 src/mightypork/gamecore/util/error/CorruptDataException.java delete mode 100644 src/mightypork/gamecore/util/error/CorruptedDataException.java rename src/mightypork/gamecore/util/math/algo/{Step.java => Move.java} (80%) create mode 100644 src/mightypork/gamecore/util/math/algo/Moves.java delete mode 100644 src/mightypork/gamecore/util/math/algo/Sides.java rename src/mightypork/gamecore/util/math/color/pal/{COMMODORE.java => CMDR.java} (96%) delete mode 100644 src/mightypork/gamecore/util/objects/ObjectUtils.java create mode 100644 src/mightypork/gamecore/util/strings/AlphanumComparator.java rename src/mightypork/rogue/screens/game/{InvLayer.java => InventoryLayer.java} (92%) create mode 100644 src/mightypork/rogue/world/gen/LevelBuilder.java delete mode 100644 src/mightypork/rogue/world/gen/LevelGenerator.java rename src/mightypork/rogue/world/gen/{RoomDesc.java => RoomEntry.java} (88%) create mode 100644 src/mightypork/rogue/world/gen/Rooms.java rename src/mightypork/rogue/world/gen/rooms/{HeartPieceRoom.java => ItemShrineRoom.java} (61%) delete mode 100644 src/mightypork/rogue/world/gen/rooms/Rooms.java create mode 100644 src/mightypork/rogue/world/gen/rooms/StorageRoom.java create mode 100644 src/mightypork/rogue/world/gen/rooms/TreasureChestRoom.java delete mode 100644 src/mightypork/rogue/world/gen/rooms/TreasureRoom.java rename src/mightypork/rogue/world/item/impl/weapons/{ItemHammer.java => ItemAxe.java} (75%) create mode 100644 src/mightypork/rogue/world/item/impl/weapons/ItemKnife.java rename src/mightypork/rogue/world/item/impl/weapons/{ItemStone.java => ItemRock.java} (79%) create mode 100644 src/mightypork/rogue/world/item/impl/weapons/ItemTwig.java create mode 100644 src/mightypork/rogue/world/tile/TileColors.java create mode 100644 src/mightypork/rogue/world/tile/impl/TileBaseChest.java create mode 100644 src/mightypork/rogue/world/tile/impl/brick/TileBrickChest.java create mode 100644 src/mightypork/rogue/world/tile/render/ChestRenderer.java diff --git a/res/img/items.png b/res/img/items.png index 6f18100c278d5ee8a15e6276b75f83b3b75790e4..71c16ae88b80ef4f119b30fb4dc509a2e0f3e69a 100644 GIT binary patch delta 2416 zcmb_e={uW=9(_eMsHLT?B^tUhrNmZDY0+AW6w@eGC26J9R4ikiio9yQgUYB9YRS|( zWo#uPVr@D?8EUi$O43MGZOITqEVuIy+)wvB&-wEEaL$MGoZoZumCBWW)iq)O1HgNi zR}QKG0LX-0^Ns+3L(2bB0(f5Z?-3g5blo00BXwBLKyiissXyCC2b7U>va|Jwom`#- zgWn5JFa`5Ms6NF@(aQJe{eWZ*y1DZby}CIMu5I}#%fAZw~r4Opmbt4c4?Qo?wrHbxkj$U#1u9* z9){PGx^L;Oyg*_sIGaYj`J=Tb<~}xxz`EESLYF|iYf(I;#`F_!DazX<4Gg3wru;!$ z{cRa>Y!kM)X*VR^LgTa%D%)vnB+RPq&xz0Nzzr0t^&PnQLV(os%gK2kr|LiGRn^%m zikr8xA`RRJid=Qz#I(f3#N#d z2g+zvH>gRgTyb8qMX#JUsgOcGD*YQ$i4W1nA!bd!YA#g?kPpk(0#yii@7{GPWfJb> zi6-cDkp5(VXKX+kuqbx@W(ZWd7CXo8^kCj)W@aXnUT;K4?1%*Cn4|1}UTt1i0bt~F z82g?p|Bw(py6FoV6G0|vy1~8NB>yLT8;R)-Ik-Xcx~WL4!(Y)!UF{&n^W+-79E5fu zrWERPiLg6$lGB}h?pT_k&%;k>`jlTM6TI_9=zeon2KzMU0+FyU%_A$2&M2`L#BLy^WS^iHDRrtM40cyzA=Ih! zzHQIitasB#xHAvfv@AjBWkD!;I8Z|&O}~T>8*YYh_W1~1aV@(>SqAK$D)tan`B(z^ zq>^6AdNQ}K`&Ts$IrkeL4+;(N2~B~+4*Rdzv zqjRCOhj*WAA~m8-UJ{DYU}FJC3p?DU(RNchZSge`U6fdaysGuVz12TCDVv^fk2>Yc z3rkkszVf$G^}>lM@@## zz=_NQ5Q*o7h39m2UnSNT1#!(A7i%lJbu;_T9)C;v^Y$W!Bjm4o_Pv!;Nn%3vPw=C3 zB3Gr|#ATAH&yG$|gWu%^__6f4l@kJA~*T8n|kauQL zYqE^m4V>ZrahThMfS8Kbk0B@&>Z<3i#6(BjG;F*TWX8lV&3g7AqwC4J#Ytd;bsNW} zaxJHL!=ItKl!klq3~+peuw&F0REw9R<4kw{%usP8$8n#SOQ&3Erckb1%DWxk`}(1V zs{sBL4=qc%AGP=9{#|3?b@mD*;#~WXIhDbRey|Ful7wF>wr82a24>VhLDMA5c@z2j zwMW4Xeb{tSUSXjRq+!#1*z+UpwUeT|;P%hWqYtM^=8w56vd%75({JLVrq%J46jp|1 zcV`*>`-8uVcm80^-f&qL>1fhH`7mrXcPTnCn146Zh>YMaIOqv39@J z`9PAbgM-7D#YO+%;PWr|{)Zmk6M6C*r9k75HG*;xE{R}0=#s{kjuo@>V4O}^N#M^f zb7o@vc+B~F?wZez6~5bzRa1H3_bdYm7@CFphd1=9+TPrv_xffhXMmVB7;bV!h(&?I zze+H$ShVPKZ=IOrLR+Cj4D6k0)}d_6Wfb!_l`b zd&Wbhv$`A6lUw~H*XL7W5pxr&Cf_q**e(iG?l4nEgoH!5CsdY7DETsrm->@uqu_pj zRAH;v&ZrTrN`9}aBMi&Ve(QhJgOO8XwLa!Jz7d=_&F#`;t)Jbo&5k;x8p>~kb$?*- zM5&3Qq_Xzry(j>3i6kw>5jb0)P+@2JFzWXgS_a4m{Bnt?T7KwIOXC7Yt02Q|m${mM zwQDc?7$HiD@m<}mt2du=95b|{^^^knT#ZYQ-ea-YhM#zE%Vj~WY_^Ws%H!geYB(Y? zPELrx=7dliIm@=&X{9~rk$f)-pyaz+l^09d)1nj*ZG#(zhHN0S%;?)71YFcmaGY&Ih z&r&i>wT|@x?M|uaTen)Ze_7ZM3+6K?AznvQ?sPyvRSW1wg^jZx-1LGyxvy`48;nw3 zPS77Zi+Rc?o-wxAbk~j>Q-E!{e}2PO)N2dvJ*|)F?H2XO@px+JIU}GFJ~$I5BEC^3 zpj_xLYidlolYUXa4&NT!C`C4Pd^ZXxIufvT>1?I^BZiXMWM#}umdOrrz%Xv?X{1Qg z3Z&Zfp!MtxWqYMQ*B)TZhO0Dv#Y=%tj`pZv_RJhPZeQ@Uoq6#=6r0 delta 2275 zcmb_eX*kr28=bLarx3|Rq%6sjOxb2!%Pm(ZCCVDvu5B#Y`B_I~jTn=$M=FGjWsH3* z##AcH7)-*oFJqZ8GhFxU|MUMm&->+kIOloJd!F}s%SBManM&%#AZ?Jx+*WlT2n6Cm z+=*}y(=$g*fY|atzs3V=8diS@uhsxnveeUeAq!bes%!LlgNu2~MwQXPa|vk+3YC$Z zAwHLu3ohdX($)JTzv{#=D&`$qekJ!gUlMyx3DRDKL@eHfLqTLIxYygffW@^4Z@}j; zwk}%rcbhbe;5MbgZA`I|p2GEwtAv3?#l`)Ddx3$kE_j;Rd>gFij0yk?zyRlaR>NfjM=ljDZ^%^J8<6o}M0O20ZH)>dDo72VMq)P-2+m z2C--wAf{XEfxM%e(V9O@s~+Lm-YK{>MBoUkURVi7kf2ne&Ra^Z6(U4HSt%B16j%sZF`e_2h2WoXn-h%t$_!_D&95v!LCTs~4r=)%CyWUnb;^v<&!iN~Qf-=N2WK zoci2oC46S)eAzQB>aPVooUDA#g73dcZ8P0mDanR;me;Jl2gkaLP zRy#JxxR7azZOQ)L9#HQ&TEU=dtq)RQZy(e;4ZM?T)r*G0?J$_%qj+G93e`Zzr1UKs zHt`;BcxfObqPTweq$>2gZr&4%1(o8TE4x@N$Wj7330vo}W;q=exbl&mdg7|7!E^Dv zAx#%B#N(jC-=QG^h?+{gjxMC4WUzMj_g$Ju{x=}HJ6{X~gN2KT?!UHf$eR~9^&kZvY*q*Q zFe^sFy%az2y0n_j9_RTmKvUVC?Pw`+yFN&r#)b$0wL=n07Qgub%H9e}ahP|qSmBk@ zaNQ*lz%>Oas5&*TEgk9$ZibxX=0IIxZl`<>O9Y(UO0)O(Zi`Pcm@=VMmtIFmYFXRmHGBnmk- zks3O5AcEZ#1opyIFv+LaSse)=k!}U7WTS35VV)PCD6Se{R-;)I(ylo^f}jTZ39M7bqA}5*oj5`(<_UOzB4}D)V%_Xj~cL~DV-xo zESk&Kn@<2fW9EJX;zmGgPN>XM>F-q$1xN4tqAw!tq*xYI5deZn#c*31)l1xq!YN?oh$qI{~Tp0y6 ztg@fo67Pu$B(KlkjlqeB3Q(6P#qasV4kbN%_KXSGf8f#Oxx^_K`?g>#qAL~b9)tVh zxNVmUgm$6Mrh#uov&q zXXUsIqThk&;Y%A146mbcvFUl)|2yRWN72#jh>;{F8@ C5Ny!^ diff --git a/res/img/items.xcf b/res/img/items.xcf index 31a7fdf50ad8ef09beccef6431b616751423c038..d7130467cafe20a7df0c9078e2f5a8053ce46fee 100644 GIT binary patch delta 1601 zcmZWpOH3PA6g@Lzo56e{0_00dLQ0^dNT5jRiZs9m6<5llmAZ&TH<(oFqH!1P&XrXO z(JuOVi%M111!_c;6(|y^TUr_-kRlufkwD$gI99#rt~D44*q*sPZvZtPOV2ZN&YgSD zz308T-FEZr=NF9GsaM}I%!ZHE4P*I*h7Ym(`-m4V^IM?i7|`$**A?}(*&Of$73v4G z*_37VhuLaLT9jp-wIn5~+j_Cq#?~4nok}m?xdlVLZ8e9hV9eyxcb9G@)hAX*AddM- z^}W?;PEM-7tuNxo;7#KY3KU z9nmf9k$klY8*?Hvm;<6C@A@3Z@G{)Ff5eb5yfJjD%fYr*?G@b5y7WxCs<9IrjYS4~ z*=Si-77>EsxRO@af^^XlbuUO4S45S9zKaEjv*M%|XX#=`$agW8cOIygCc;Y4#R8L`TW0>t{4|S`zdHpZ-g<8yIRWaXx9NL1o!!} zrdwd7h{PhkArx~O<%QI3Z9^>5t!;?(q7`oHC#`1>VptQr1)VDO^Btc+x1RihU0`co zHwQmeaq6T$crV6HveRQlQlXu|z-UQOF)cxTLq)L-^s{2;u# zXWeLh-iVwwqRxKSh@5Xko$jyBwzUxk@Ar%#NwYNmzY7$(JAaa#zkLCupEN+XPmb=} zo$&mTvn)scIy-HEXXA`6a?n@Pq(&<{UTa{%02jwv5{ui~6mNif4XHpSAA|bQ-bXF5 zP%SQZN(ccl1}gW7kQiTvyH4{s^@v<#wOVEk?7nO1i%wh`fLv6U!Y7R$bv1lBzM}il zC)DJk0RK}Y94KD|#qf(p1%@0&*cTS&M($&7Q|gKMwR*2ar?&2wyFmEeB-&@FTVcV`zru64suOAjW>=E`+#ed>nnA&{?dQ?(`R`A delta 1053 zcma)5U1$_n6#nkq-O0?ZRY(;4X&QyBrip@@L`7_?ZXXs@%&Wl{DOoEBNu{0}ng8|8i5)&OmXI$qbs43Xq*?F`Rmz(j_kuUsr+;HLrn8Ee%EKZtcmKjl6v@vjStviL7K;!51P4<-l)_R-m1S99 z$(aHxRLid)UpWAVkP0p;#KfHNycFTngkh)>Jb$KAsJwn~8A%w#1ivdRqqPiAR!gNv zSCLFoa$gN8d`*0X_}atYkV*cC5N(AnB5UyOU7<~ak7p(SQuF7F7kP z0g_qwf(9JGX2|;#!c+i-6_6b|qW*I9p9;$xIs6WEXu`n;FlPh&5$6uUz)4fha)hM$ z&h24c@JLCAjHIl?B5f_AQvb0Q8Z*1Z8>^L1=bwrBc8phYd9l%6qDSLJK9_%%r_tDF zx03&A7c?`O7eCoClflpD#>H)WsmUS75f;zwE&U|(Gstw3#pyT*r>vXT7kn|-TGO(rahvs3u3HFb*+F*WL%!u8aVN-r LCc7+tnAi6XapkUu diff --git a/res/img/tiles.png b/res/img/tiles.png index f3621ab758983c0feaefc34e603aa0896be2483f..c3e14e5e237c4734161f5c42f7a4ee61f6005df2 100644 GIT binary patch literal 9764 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ðq$GU&BjfwIn{H(CA^NW7PG7MR|LN@39^V-C@ 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 literal 9092 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 z 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 literal 23771 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 HWH0|G$+$Nw diff --git a/src/mightypork/gamecore/app/BaseApp.java b/src/mightypork/gamecore/app/BaseApp.java index ffab61a..bf50d74 100644 --- a/src/mightypork/gamecore/app/BaseApp.java +++ b/src/mightypork/gamecore/app/BaseApp.java @@ -21,7 +21,7 @@ import mightypork.gamecore.util.annot.DefaultImpl; import mightypork.gamecore.util.files.InstanceLock; import mightypork.gamecore.util.ion.Ion; import mightypork.gamecore.util.math.algo.Coord; -import mightypork.gamecore.util.math.algo.Step; +import mightypork.gamecore.util.math.algo.Move; /** @@ -153,7 +153,7 @@ public abstract class BaseApp implements AppAccess, UncaughtExceptionHandler { protected void registerIonizables() { Ion.registerType(Coord.ION_MARK, Coord.class); - Ion.registerType(Step.ION_MARK, Step.class); + Ion.registerType(Move.ION_MARK, Move.class); } diff --git a/src/mightypork/gamecore/eventbus/EventBus.java b/src/mightypork/gamecore/eventbus/EventBus.java index 988af77..8f55824 100644 --- a/src/mightypork/gamecore/eventbus/EventBus.java +++ b/src/mightypork/gamecore/eventbus/EventBus.java @@ -15,6 +15,7 @@ import mightypork.gamecore.eventbus.event_flags.ImmediateEvent; import mightypork.gamecore.eventbus.event_flags.NotLoggedEvent; import mightypork.gamecore.eventbus.events.Destroyable; import mightypork.gamecore.logging.Log; +import mightypork.gamecore.util.Utils; /** @@ -142,10 +143,6 @@ final public class EventBus implements Destroyable, BusAccess { /** Messages queued for delivery */ private final DelayQueue sendQueue = new DelayQueue<>(); - private volatile boolean sendingDirect = false; - - private BusEvent lastEvt; - /** * Make a new bus and start it's queue thread. @@ -180,13 +177,13 @@ final public class EventBus implements Destroyable, BusAccess { { assertLive(); - final DelayedEvent adelay = event.getClass().getAnnotation(DelayedEvent.class); + final DelayedEvent adelay = Utils.getAnnotation(event, DelayedEvent.class); if (adelay != null) { sendDelayed(event, adelay.delay()); return; } - if (event.getClass().isAnnotationPresent(ImmediateEvent.class)) { + if (Utils.hasAnnotation(event, ImmediateEvent.class)) { sendDirect(event); return; } @@ -364,7 +361,6 @@ final public class EventBus implements Destroyable, BusAccess { for (int i = 0; i < 2; i++) { // two tries. -// channels.setBuffering(true); for (final EventChannel b : channels) { if (b.canBroadcast(event)) { accepted = true; @@ -373,7 +369,6 @@ final public class EventBus implements Destroyable, BusAccess { if (event.isConsumed()) break; } -// channels.setBuffering(false); if (!accepted) if (addChannelForEvent(event)) continue; @@ -388,7 +383,7 @@ final public class EventBus implements Destroyable, BusAccess { private boolean shallLog(BusEvent event) { if (!detailedLogging) return false; - if (event.getClass().isAnnotationPresent(NotLoggedEvent.class)) return false; + if (Utils.hasAnnotation(event, NotLoggedEvent.class)) return false; return true; } diff --git a/src/mightypork/gamecore/util/Utils.java b/src/mightypork/gamecore/util/Utils.java index 49b1c38..f8729e4 100644 --- a/src/mightypork/gamecore/util/Utils.java +++ b/src/mightypork/gamecore/util/Utils.java @@ -1,6 +1,9 @@ package mightypork.gamecore.util; +import java.lang.annotation.Annotation; + + /** * Assorted utils * @@ -14,4 +17,32 @@ public final class Utils { t.start(); return t; } + + + public static boolean hasAnnotation(Object tested, Class annotation) + { + return tested.getClass().isAnnotationPresent(annotation); + } + + + public static T getAnnotation(Object tested, Class annotation) + { + return tested.getClass().getAnnotation(annotation); + } + + + /** + * Pick first non-null option + * + * @param options options + * @return the selected option + */ + public static Object fallback(Object... options) + { + for (final Object o : options) { + if (o != null) return o; + } + + return null; // all null + } } diff --git a/src/mightypork/gamecore/util/error/CorruptDataException.java b/src/mightypork/gamecore/util/error/CorruptDataException.java new file mode 100644 index 0000000..5c1383c --- /dev/null +++ b/src/mightypork/gamecore/util/error/CorruptDataException.java @@ -0,0 +1,37 @@ +package mightypork.gamecore.util.error; + + +import java.io.IOException; + + +/** + * Thrown when data could not be read successfully. + * + * @author MightyPork + */ +public class CorruptDataException extends IOException { + + public CorruptDataException() + { + super(); + } + + + public CorruptDataException(String message, Throwable cause) + { + super(message, cause); + } + + + public CorruptDataException(String message) + { + super(message); + } + + + public CorruptDataException(Throwable cause) + { + super(cause); + } + +} diff --git a/src/mightypork/gamecore/util/error/CorruptedDataException.java b/src/mightypork/gamecore/util/error/CorruptedDataException.java deleted file mode 100644 index c026c9b..0000000 --- a/src/mightypork/gamecore/util/error/CorruptedDataException.java +++ /dev/null @@ -1,37 +0,0 @@ -package mightypork.gamecore.util.error; - - -import java.io.IOException; - - -/** - * To be used when a data could not be read successfully. - * - * @author MightyPork - */ -public class CorruptedDataException extends IOException { - - public CorruptedDataException() - { - super(); - } - - - public CorruptedDataException(String message, Throwable cause) - { - super(message, cause); - } - - - public CorruptedDataException(String message) - { - super(message); - } - - - public CorruptedDataException(Throwable cause) - { - super(cause); - } - -} diff --git a/src/mightypork/gamecore/util/error/IllegalValueException.java b/src/mightypork/gamecore/util/error/IllegalValueException.java index 74d03bc..9add26c 100644 --- a/src/mightypork/gamecore/util/error/IllegalValueException.java +++ b/src/mightypork/gamecore/util/error/IllegalValueException.java @@ -1,6 +1,12 @@ package mightypork.gamecore.util.error; +/** + * Thrown when a invalid value is given to a method, or found in a data object / + * file etc + * + * @author MightyPork + */ public class IllegalValueException extends RuntimeException { public IllegalValueException() @@ -25,10 +31,4 @@ public class IllegalValueException extends RuntimeException { super(message, cause); } - - public IllegalValueException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) - { - super(message, cause, enableSuppression, writableStackTrace); - } - } diff --git a/src/mightypork/gamecore/util/error/KeyAlreadyExistsException.java b/src/mightypork/gamecore/util/error/KeyAlreadyExistsException.java index ac8c26c..2cac099 100644 --- a/src/mightypork/gamecore/util/error/KeyAlreadyExistsException.java +++ b/src/mightypork/gamecore/util/error/KeyAlreadyExistsException.java @@ -1,6 +1,11 @@ package mightypork.gamecore.util.error; +/** + * Thrown by a map-like class when the key specified is already taken. + * + * @author MightyPork + */ public class KeyAlreadyExistsException extends RuntimeException { public KeyAlreadyExistsException() diff --git a/src/mightypork/gamecore/util/ion/IonInput.java b/src/mightypork/gamecore/util/ion/IonInput.java index 278259c..c4eef32 100644 --- a/src/mightypork/gamecore/util/ion/IonInput.java +++ b/src/mightypork/gamecore/util/ion/IonInput.java @@ -10,7 +10,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.Map; -import mightypork.gamecore.util.error.CorruptedDataException; +import mightypork.gamecore.util.error.CorruptDataException; /** @@ -360,7 +360,7 @@ public class IonInput { return readStrings(); default: - throw new CorruptedDataException("Invalid mark: " + mark); + throw new CorruptDataException("Invalid mark: " + mark); } } @@ -378,7 +378,7 @@ public class IonInput { if (mark == Ion.ENTRY) return true; if (mark == Ion.END) return false; - throw new CorruptedDataException("Unexpected mark in sequence: " + mark); + throw new CorruptDataException("Unexpected mark in sequence: " + mark); } @@ -411,7 +411,7 @@ public class IonInput { } return filled; } catch (final ClassCastException e) { - throw new CorruptedDataException("Unexpected element type in sequence.", e); + throw new CorruptDataException("Unexpected element type in sequence.", e); } } @@ -448,7 +448,7 @@ public class IonInput { } return filled; } catch (final ClassCastException e) { - throw new CorruptedDataException("Unexpected element type in map.", e); + throw new CorruptDataException("Unexpected element type in map.", e); } } } diff --git a/src/mightypork/gamecore/util/math/Calc.java b/src/mightypork/gamecore/util/math/Calc.java index 718f500..1d40931 100644 --- a/src/mightypork/gamecore/util/math/Calc.java +++ b/src/mightypork/gamecore/util/math/Calc.java @@ -619,6 +619,19 @@ public class Calc { * @return picked element */ public static T pick(List list) + { + return pick(rand, list); + } + + + /** + * Pick random element from a given list. + * + * @param rand RNG + * @param list list of choices + * @return picked element + */ + public static T pick(Random rand, List list) { if (list.size() == 0) return null; return list.get(rand.nextInt(list.size())); @@ -718,4 +731,14 @@ public class Calc { return out; } + + + public static int countBits(byte b) + { + int c = 0; + for (int i = 0; i < 8; i++) { + c += (b >> i) & 1; + } + return c; + } } diff --git a/src/mightypork/gamecore/util/math/Range.java b/src/mightypork/gamecore/util/math/Range.java index 559edde..70318a4 100644 --- a/src/mightypork/gamecore/util/math/Range.java +++ b/src/mightypork/gamecore/util/math/Range.java @@ -1,6 +1,9 @@ package mightypork.gamecore.util.math; +import java.util.Random; + + /** * Numeric range, able to generate random numbers and give min/max values. * @@ -71,7 +74,7 @@ public class Range { */ public int randInt() { - return Calc.randInt(Calc.rand, (int) Math.round(min), (int) Math.round(min)); + return randInt(Calc.rand); } @@ -82,7 +85,31 @@ public class Range { */ public double randDouble() { - return min + Calc.rand.nextDouble() * (max - min); + return randDouble(Calc.rand); + } + + + /** + * Get random integer from range + * + * @param rand RNG + * @return random int + */ + public int randInt(Random rand) + { + return Calc.randInt(rand, (int) Math.round(min), (int) Math.round(min)); + } + + + /** + * Get random double from this range + * + * @param rand RNG + * @return random double + */ + public double randDouble(Random rand) + { + return min + rand.nextDouble() * (max - min); } diff --git a/src/mightypork/gamecore/util/math/algo/Coord.java b/src/mightypork/gamecore/util/math/algo/Coord.java index a09e839..7c53a8b 100644 --- a/src/mightypork/gamecore/util/math/algo/Coord.java +++ b/src/mightypork/gamecore/util/math/algo/Coord.java @@ -87,7 +87,7 @@ public class Coord implements IonObjBundled, IonObjBinary { } - public Coord add(Step added) + public Coord add(Move added) { return add(added.x(), added.y()); } diff --git a/src/mightypork/gamecore/util/math/algo/Step.java b/src/mightypork/gamecore/util/math/algo/Move.java similarity index 80% rename from src/mightypork/gamecore/util/math/algo/Step.java rename to src/mightypork/gamecore/util/math/algo/Move.java index ce20118..d78535b 100644 --- a/src/mightypork/gamecore/util/math/algo/Step.java +++ b/src/mightypork/gamecore/util/math/algo/Move.java @@ -16,18 +16,18 @@ import mightypork.gamecore.util.ion.IonOutput; * * @author MightyPork */ -public class Step implements IonObjBinary, IonObjBundled { +public class Move implements IonObjBinary, IonObjBundled { public static final int ION_MARK = 254; - public static final Step NORTH = new Step(0, -1); - public static final Step SOUTH = new Step(0, 1); - public static final Step EAST = new Step(1, 0); - public static final Step WEST = new Step(-1, 0); - public static final Step NONE = new Step(0, 0); + public static final Move NORTH = new Move(0, -1); + public static final Move SOUTH = new Move(0, 1); + public static final Move EAST = new Move(1, 0); + public static final Move WEST = new Move(-1, 0); + public static final Move NONE = new Move(0, 0); - public static Step make(int x, int y) + public static Move make(int x, int y) { x = x < 0 ? -1 : x > 0 ? 1 : 0; y = y < 0 ? -1 : y > 0 ? 1 : 0; @@ -38,20 +38,20 @@ public class Step implements IonObjBinary, IonObjBundled { if (x == 1 && y == 0) return EAST; if (x == 0 && y == 0) return NONE; - return new Step(x, y); + return new Move(x, y); } private byte x; private byte y; - public Step() + public Move() { // for ion } - public Step(int x, int y) + public Move(int x, int y) { this.x = (byte) (x < 0 ? -1 : x > 0 ? 1 : 0); this.y = (byte) (y < 0 ? -1 : y > 0 ? 1 : 0); diff --git a/src/mightypork/gamecore/util/math/algo/Moves.java b/src/mightypork/gamecore/util/math/algo/Moves.java new file mode 100644 index 0000000..08f1911 --- /dev/null +++ b/src/mightypork/gamecore/util/math/algo/Moves.java @@ -0,0 +1,95 @@ +package mightypork.gamecore.util.math.algo; + + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Random; + +import mightypork.gamecore.util.math.Calc; + + +/** + * Move lists, bit masks and other utilities + * + * @author MightyPork + */ +public class Moves { + + public static final byte BIT_NW = (byte) 0b10000000; + public static final byte BIT_N = (byte) 0b01000000; + public static final byte BIT_NE = (byte) 0b00100000; + public static final byte BIT_E = (byte) 0b00010000; + public static final byte BIT_SE = (byte) 0b00001000; + public static final byte BIT_S = (byte) 0b00000100; + public static final byte BIT_SW = (byte) 0b00000010; + public static final byte BIT_W = (byte) 0b00000001; + + public static final byte BITS_CARDINAL = BIT_N | BIT_S | BIT_E | BIT_W; + public static final byte BITS_DIAGONAL = BIT_NE | BIT_NW | BIT_SE | BIT_SW; + + public static final byte BITS_NW_CORNER = BIT_W | BIT_NW | BIT_N; + public static final byte BITS_NE_CORNER = BIT_E | BIT_NE | BIT_N; + public static final byte BITS_SW_CORNER = BIT_W | BIT_SW | BIT_S; + public static final byte BITS_SE_CORNER = BIT_E | BIT_SE | BIT_S; + + public static final Move NW = Move.make(-1, -1); + public static final Move N = Move.make(0, -1); + public static final Move NE = Move.make(1, -1); + public static final Move E = Move.make(1, 0); + public static final Move SE = Move.make(1, 1); + public static final Move S = Move.make(0, 1); + public static final Move SW = Move.make(-1, 1); + public static final Move W = Move.make(-1, 0); + + //@formatter:off + /** All sides, in the order of bits. */ + public final static List ALL_SIDES = Collections.unmodifiableList(Arrays.asList( + NW, + N, + NE, + E, + SE, + S, + SW, + W + )); + + public final static List CARDINAL_SIDES = Collections.unmodifiableList(Arrays.asList( + N, + E, + S, + W + )); + + //@formatter:on + + /** + * Get element from all sides + * + * @param i side index + * @return the side coord + */ + public static Move getSide(int i) + { + return ALL_SIDES.get(i); + } + + + public static byte getBit(int i) + { + return (byte) (1 << (7 - i)); + } + + + public static Move randomCardinal() + { + return Calc.pick(CARDINAL_SIDES); + } + + + public static Move randomCardinal(Random rand) + { + return Calc.pick(rand, CARDINAL_SIDES); + } +} diff --git a/src/mightypork/gamecore/util/math/algo/Sides.java b/src/mightypork/gamecore/util/math/algo/Sides.java deleted file mode 100644 index f397c7b..0000000 --- a/src/mightypork/gamecore/util/math/algo/Sides.java +++ /dev/null @@ -1,79 +0,0 @@ -package mightypork.gamecore.util.math.algo; - - -import mightypork.gamecore.util.math.Calc; - - -public class Sides { - - public static final byte MASK_NW = (byte) 0b10000000; - public static final byte MASK_N = (byte) 0b01000000; - public static final byte MASK_NE = (byte) 0b00100000; - public static final byte MASK_E = (byte) 0b00010000; - public static final byte MASK_SE = (byte) 0b00001000; - public static final byte MASK_S = (byte) 0b00000100; - public static final byte MASK_SW = (byte) 0b00000010; - public static final byte MASK_W = (byte) 0b00000001; - - public static final byte MASK_CARDINAL = MASK_N | MASK_S | MASK_E | MASK_W; - public static final byte MASK_DIAGONAL = MASK_NE | MASK_NW | MASK_SE | MASK_SW; - - public static final byte NW_CORNER = MASK_W | MASK_NW | MASK_N; - public static final byte NE_CORNER = MASK_E | MASK_NE | MASK_N; - public static final byte SW_CORNER = MASK_W | MASK_SW | MASK_S; - public static final byte SE_CORNER = MASK_E | MASK_SE | MASK_S; - - public static final Step NW = Step.make(-1, -1); - public static final Step N = Step.make(0, -1); - public static final Step NE = Step.make(1, -1); - public static final Step E = Step.make(1, 0); - public static final Step SE = Step.make(1, 1); - public static final Step S = Step.make(0, 1); - public static final Step SW = Step.make(-1, 1); - public static final Step W = Step.make(-1, 0); - - //@formatter:off - /** All sides, in the order of bits. */ - public final static Step[] ALL_SIDES = { - NW, - N, - NE, - E, - SE, - S, - SW, - W - }; - - public final static Step[] CARDINAL_SIDES = { - N, - E, - S, - W - }; - - //@formatter:on - - /** - * Get element from all sides - * - * @param i side index - * @return the side coord - */ - public static Step get(int i) - { - return ALL_SIDES[i]; - } - - - public static byte bit(int i) - { - return (byte) (1 << (7 - i)); - } - - - public static Step randomCardinal() - { - return CARDINAL_SIDES[Calc.randInt(0, 3)]; - } -} diff --git a/src/mightypork/gamecore/util/math/algo/floodfill/FloodFill.java b/src/mightypork/gamecore/util/math/algo/floodfill/FloodFill.java index 4539354..e6a84fc 100644 --- a/src/mightypork/gamecore/util/math/algo/floodfill/FloodFill.java +++ b/src/mightypork/gamecore/util/math/algo/floodfill/FloodFill.java @@ -3,10 +3,11 @@ package mightypork.gamecore.util.math.algo.floodfill; import java.util.Collection; import java.util.LinkedList; +import java.util.List; import java.util.Queue; import mightypork.gamecore.util.math.algo.Coord; -import mightypork.gamecore.util.math.algo.Step; +import mightypork.gamecore.util.math.algo.Move; public abstract class FloodFill { @@ -17,7 +18,7 @@ public abstract class FloodFill { public abstract boolean canSpreadFrom(Coord pos); - public abstract Step[] getSpreadSides(); + public abstract List getSpreadSides(); /** @@ -49,7 +50,6 @@ public abstract class FloodFill { activeNodes.add(start); - final Step[] sides = getSpreadSides(); boolean forceSpreadNext = forceSpreadStart(); boolean limitReached = false; @@ -62,7 +62,7 @@ public abstract class FloodFill { forceSpreadNext = false; - for (final Step spr : sides) { + for (final Move spr : getSpreadSides()) { final Coord next = current.add(spr); if (activeNodes.contains(next) || foundNodes.contains(next)) continue; diff --git a/src/mightypork/gamecore/util/math/algo/pathfinding/PathFinder.java b/src/mightypork/gamecore/util/math/algo/pathfinding/PathFinder.java index eb9eb86..8173d06 100644 --- a/src/mightypork/gamecore/util/math/algo/pathfinding/PathFinder.java +++ b/src/mightypork/gamecore/util/math/algo/pathfinding/PathFinder.java @@ -8,7 +8,7 @@ import java.util.LinkedList; import java.util.List; import mightypork.gamecore.util.math.algo.Coord; -import mightypork.gamecore.util.math.algo.Step; +import mightypork.gamecore.util.math.algo.Move; import mightypork.gamecore.util.math.algo.pathfinding.heuristics.DiagonalHeuristic; import mightypork.gamecore.util.math.algo.pathfinding.heuristics.ManhattanHeuristic; @@ -29,24 +29,24 @@ public abstract class PathFinder { private boolean ignoreEnd; - public List findPathRelative(Coord start, Coord end) + public List findPathRelative(Coord start, Coord end) { return findPathRelative(start, end, ignoreStart, ignoreEnd); } - public List findPathRelative(Coord start, Coord end, boolean ignoreStart, boolean ignoreEnd) + public List findPathRelative(Coord start, Coord end, boolean ignoreStart, boolean ignoreEnd) { final List path = findPath(start, end, ignoreStart, ignoreEnd); if (path == null) return null; - final List out = new ArrayList<>(); + final List out = new ArrayList<>(); final Coord current = start.copy(); for (final Coord c : path) { if (c.equals(current)) continue; - out.add(Step.make(c.x - current.x, c.y - current.y)); + out.add(Move.make(c.x - current.x, c.y - current.y)); current.x = c.x; current.y = c.y; } @@ -76,8 +76,6 @@ public abstract class PathFinder { open.add(n); } - final Step[] walkDirs = getWalkSides(); - Node current = null; while (true) { @@ -93,7 +91,7 @@ public abstract class PathFinder { break; } - for (final Step go : walkDirs) { + for (final Move go : getWalkSides()) { final Coord c = current.pos.add(go); if (!isAccessible(c) && !(c.equals(end) && ignoreEnd) && !(c.equals(start) && ignoreStart)) continue; @@ -225,7 +223,7 @@ public abstract class PathFinder { protected abstract Heuristic getHeuristic(); - protected abstract Step[] getWalkSides(); + protected abstract List getWalkSides(); /** diff --git a/src/mightypork/gamecore/util/math/algo/pathfinding/PathFinderProxy.java b/src/mightypork/gamecore/util/math/algo/pathfinding/PathFinderProxy.java index 3e56461..163966b 100644 --- a/src/mightypork/gamecore/util/math/algo/pathfinding/PathFinderProxy.java +++ b/src/mightypork/gamecore/util/math/algo/pathfinding/PathFinderProxy.java @@ -1,8 +1,10 @@ package mightypork.gamecore.util.math.algo.pathfinding; +import java.util.List; + import mightypork.gamecore.util.math.algo.Coord; -import mightypork.gamecore.util.math.algo.Step; +import mightypork.gamecore.util.math.algo.Move; /** @@ -51,7 +53,7 @@ public class PathFinderProxy extends PathFinder { @Override - protected Step[] getWalkSides() + protected List getWalkSides() { return source.getWalkSides(); } diff --git a/src/mightypork/gamecore/util/math/color/pal/COMMODORE.java b/src/mightypork/gamecore/util/math/color/pal/CMDR.java similarity index 96% rename from src/mightypork/gamecore/util/math/color/pal/COMMODORE.java rename to src/mightypork/gamecore/util/math/color/pal/CMDR.java index ffb0166..63a8833 100644 --- a/src/mightypork/gamecore/util/math/color/pal/COMMODORE.java +++ b/src/mightypork/gamecore/util/math/color/pal/CMDR.java @@ -9,7 +9,7 @@ import mightypork.gamecore.util.math.color.Color; * * @author MightyPork */ -public interface COMMODORE { +public interface CMDR { Color BLACK = Color.fromHex(0x040013); Color WHITE = Color.fromHex(0xFFFFFF); diff --git a/src/mightypork/gamecore/util/math/color/pal/RGB.java b/src/mightypork/gamecore/util/math/color/pal/RGB.java index ac81669..cdbdaf5 100644 --- a/src/mightypork/gamecore/util/math/color/pal/RGB.java +++ b/src/mightypork/gamecore/util/math/color/pal/RGB.java @@ -38,6 +38,7 @@ public class RGB { public static final Color PINK = Color.fromHex(0xFF3FFC); public static final Color ORANGE = Color.fromHex(0xFC4800); + public static final Color BROWN = Color.fromHex(0x83501B); public static final Color NONE = Color.rgba(0, 0, 0, 0); } diff --git a/src/mightypork/gamecore/util/objects/ObjectUtils.java b/src/mightypork/gamecore/util/objects/ObjectUtils.java deleted file mode 100644 index 15d5aff..0000000 --- a/src/mightypork/gamecore/util/objects/ObjectUtils.java +++ /dev/null @@ -1,50 +0,0 @@ -package mightypork.gamecore.util.objects; - - -import java.util.ArrayList; -import java.util.List; - -import mightypork.gamecore.logging.Log; - - -/** - * Object utils class - * - * @author MightyPork - */ -public class ObjectUtils { - - public static Object fallback(Object... options) - { - for (final Object o : options) { - if (o != null) return o; - } - return null; // error - } - - - public static String arrayToString(T[] arr) - { - final StringBuilder sb = new StringBuilder(); - - sb.append('['); - final boolean first = true; - for (final T o : arr) { - if (!first) sb.append(','); - sb.append(Log.str(o)); - } - sb.append(']'); - - return sb.toString(); - } - - - public static List arrayToList(T[] objs) - { - final ArrayList list = new ArrayList<>(); - for (final T o : objs) { - list.add(o); - } - return list; - } -} diff --git a/src/mightypork/gamecore/util/strings/AlphanumComparator.java b/src/mightypork/gamecore/util/strings/AlphanumComparator.java new file mode 100644 index 0000000..7c3d87e --- /dev/null +++ b/src/mightypork/gamecore/util/strings/AlphanumComparator.java @@ -0,0 +1,119 @@ +/* + * The Alphanum Algorithm is an improved sorting algorithm for strings + * containing numbers. Instead of sorting numbers in ASCII order like + * a standard sort, this algorithm sorts numbers in numeric order. + * + * The Alphanum Algorithm is discussed at http://www.DaveKoelle.com + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +package mightypork.gamecore.util.strings; + + +import java.util.Comparator; + + +/** + * String comparator taking care of strings with numbers. + * + * @author Daniel Migowski + * @author Andre Bogus + * @author David Koelle + * @author MightyPork + */ +public class AlphanumComparator implements Comparator { + + public static final AlphanumComparator instance = new AlphanumComparator(); + + + private final boolean isDigit(char ch) + { + return ch >= '0' && ch <= '9'; + } + + + /** + * Length of string is passed in for improved efficiency (only need to + * calculate it once) + **/ + private final String getChunk(String s, int slength, int marker) + { + final StringBuilder chunk = new StringBuilder(); + char c = s.charAt(marker); + chunk.append(c); + marker++; + + if (isDigit(c)) { + while (marker < slength) { + c = s.charAt(marker); + if (!isDigit(c)) break; + chunk.append(c); + marker++; + } + } else { + while (marker < slength) { + c = s.charAt(marker); + if (isDigit(c)) break; + chunk.append(c); + marker++; + } + } + return chunk.toString(); + } + + + @Override + public int compare(String s1, String s2) + { + int thisMarker = 0; + int thatMarker = 0; + final int s1Length = s1.length(); + final int s2Length = s2.length(); + + while (thisMarker < s1Length && thatMarker < s2Length) { + final String thisChunk = getChunk(s1, s1Length, thisMarker); + thisMarker += thisChunk.length(); + + final String thatChunk = getChunk(s2, s2Length, thatMarker); + thatMarker += thatChunk.length(); + + // If both chunks contain numeric characters, sort them numerically + int result = 0; + if (isDigit(thisChunk.charAt(0)) && isDigit(thatChunk.charAt(0))) { + // Simple chunk comparison by length. + final int thisChunkLength = thisChunk.length(); + result = thisChunkLength - thatChunk.length(); + // If equal, the first different number counts + if (result == 0) { + for (int i = 0; i < thisChunkLength; i++) { + result = thisChunk.charAt(i) - thatChunk.charAt(i); + if (result != 0) { + return result; + } + } + } + } else { + result = thisChunk.compareTo(thatChunk); + } + + if (result != 0) return result; + } + + return s1Length - s2Length; + } +} diff --git a/src/mightypork/rogue/GameLoop.java b/src/mightypork/rogue/GameLoop.java index 8ac684a..db88b6f 100644 --- a/src/mightypork/rogue/GameLoop.java +++ b/src/mightypork/rogue/GameLoop.java @@ -50,7 +50,7 @@ public final class GameLoop extends MainLoop implements ActionRequest.Listener { @Override public void execute() { - Res.getEffect("gui.shutter").play(1); + Res.getSoundEffect("gui.shutter").play(1); Utils.runAsThread(new TaskTakeScreenshot()); } }; diff --git a/src/mightypork/rogue/Res.java b/src/mightypork/rogue/Res.java index f146b86..027e726 100644 --- a/src/mightypork/rogue/Res.java +++ b/src/mightypork/rogue/Res.java @@ -60,9 +60,8 @@ public final class Res { fonts.loadFont("tinyutf", font = new DeferredFont("/res/font/TinyUnicode2.ttf", Glyphs.basic, 16)); font.setDiscardRatio(6 / 16D, 2 / 16D); - // aliases based on concrete usage + // aliases fonts.addAlias("thick", "press_start"); - fonts.addAlias("thin", "battlenet"); fonts.addAlias("tiny", "tinyutf"); } @@ -135,6 +134,8 @@ public final class Res { textures.add("tile.brick.passage", grid.makeSheet(3, 2, 4, 1)); textures.add("tile.brick.stairs.up", grid.makeQuad(0, 6)); textures.add("tile.brick.stairs.down", grid.makeQuad(1, 6)); + textures.add("tile.extra.chest.closed", grid.makeQuad(0, 4)); + textures.add("tile.extra.chest.open", grid.makeQuad(1, 4)); // shadows textures.add("tile.shadow.n", grid.makeQuad(0, 7)); @@ -165,12 +166,14 @@ public final class Res { textures.add("item.meat", grid.makeQuad(0, 0)); textures.add("item.club", grid.makeQuad(1, 0)); textures.add("item.sword", grid.makeQuad(2, 0)); - textures.add("item.hammer", grid.makeQuad(3, 0)); + textures.add("item.axe", grid.makeQuad(3, 0)); textures.add("item.stone", grid.makeQuad(4, 0)); textures.add("item.bone", grid.makeQuad(5, 0)); textures.add("item.cheese", grid.makeQuad(6, 0)); textures.add("item.sandwich", grid.makeQuad(7, 0)); textures.add("item.heart_piece", grid.makeQuad(0, 1)); + textures.add("item.knife", grid.makeQuad(1, 1)); + textures.add("item.twig", grid.makeQuad(2, 1)); } @@ -192,7 +195,7 @@ public final class Res { * @param key * @return sheet */ - public static TxSheet txs(String key) + public static TxSheet getTxSheet(String key) { return textures.getSheet(key); } @@ -204,19 +207,19 @@ public final class Res { * @param key * @return quad */ - public static TxQuad txq(String key) + public static TxQuad getTxQuad(String key) { return textures.getQuad(key); } - public static LoopPlayer getLoop(String key) + public static LoopPlayer getSoundLoop(String key) { return sounds.getLoop(key); } - public static EffectPlayer getEffect(String key) + public static EffectPlayer getSoundEffect(String key) { return sounds.getEffect(key); } diff --git a/src/mightypork/rogue/screens/game/HudLayer.java b/src/mightypork/rogue/screens/game/HudLayer.java index 95564ea..46f65e1 100644 --- a/src/mightypork/rogue/screens/game/HudLayer.java +++ b/src/mightypork/rogue/screens/game/HudLayer.java @@ -84,9 +84,9 @@ public class HudLayer extends ScreenLayer { final HeartBar hearts = new HeartBar( playerHealthTotal, playerHealthActive, - Res.txq("hud.heart.on"), - Res.txq("hud.heart.half"), - Res.txq("hud.heart.off"), + Res.getTxQuad("hud.heart.on"), + Res.getTxQuad("hud.heart.half"), + Res.getTxQuad("hud.heart.off"), AlignX.LEFT); //@formatter:on @@ -117,13 +117,13 @@ public class HudLayer extends ScreenLayer { NavButton btn; - nav.addRight(btn = new NavButton(Res.txq("nav.button.fg.inventory"))); + nav.addRight(btn = new NavButton(Res.getTxQuad("nav.button.fg.inventory"))); btn.setAction(gameScreen.actionToggleInv); - nav.addRight(btn = new NavButton(Res.txq("nav.button.fg.eat"))); + nav.addRight(btn = new NavButton(Res.getTxQuad("nav.button.fg.eat"))); btn.setAction(gameScreen.actionEat); - nav.addRight(btn = new NavButton(Res.txq("nav.button.fg.pause"))); + nav.addRight(btn = new NavButton(Res.getTxQuad("nav.button.fg.pause"))); btn.setAction(gameScreen.actionTogglePause); @@ -131,19 +131,19 @@ public class HudLayer extends ScreenLayer { //nav.addLeft(new NavButton(Res.txq("nav.button.fg.options"))); //nav.addLeft(new NavButton(Res.txq("nav.button.fg.help"))); - nav.addLeft(btn = new NavButton(Res.txq("nav.button.fg.menu"))); + nav.addLeft(btn = new NavButton(Res.getTxQuad("nav.button.fg.menu"))); btn.setAction(gameScreen.actionMenu); - nav.addLeft(btn = new NavButton(Res.txq("nav.button.fg.save"))); + nav.addLeft(btn = new NavButton(Res.getTxQuad("nav.button.fg.save"))); btn.setAction(gameScreen.actionSave); - nav.addLeft(btn = new NavButton(Res.txq("nav.button.fg.load"))); + nav.addLeft(btn = new NavButton(Res.getTxQuad("nav.button.fg.load"))); btn.setAction(gameScreen.actionLoad); - nav.addLeft(btn = new NavButton(Res.txq("nav.button.fg.map"))); + nav.addLeft(btn = new NavButton(Res.getTxQuad("nav.button.fg.map"))); btn.setAction(gameScreen.actionToggleMinimap); - nav.addLeft(btn = new NavButton(Res.txq("nav.button.fg.magnify"))); + nav.addLeft(btn = new NavButton(Res.getTxQuad("nav.button.fg.magnify"))); btn.setAction(gameScreen.actionToggleZoom); } diff --git a/src/mightypork/rogue/screens/game/IngameNav.java b/src/mightypork/rogue/screens/game/IngameNav.java index 1626eb3..d512344 100644 --- a/src/mightypork/rogue/screens/game/IngameNav.java +++ b/src/mightypork/rogue/screens/game/IngameNav.java @@ -43,7 +43,7 @@ public class IngameNav extends LayoutComponent { paintHelper = leftEdge().growRight(height().mul(4)); - bg = Res.txq("nav.bg"); + bg = Res.getTxQuad("nav.bg"); } diff --git a/src/mightypork/rogue/screens/game/InvSlot.java b/src/mightypork/rogue/screens/game/InvSlot.java index 8ae5fe2..1002a94 100644 --- a/src/mightypork/rogue/screens/game/InvSlot.java +++ b/src/mightypork/rogue/screens/game/InvSlot.java @@ -50,8 +50,8 @@ public class InvSlot extends ClickableComponent { public InvSlot(int index, InvSlot[] allSlots) { super(); - this.txBase = Res.txq("inv.slot.base"); - this.txSelected = Res.txq("inv.slot.selected"); + this.txBase = Res.getTxQuad("inv.slot.base"); + this.txSelected = Res.getTxQuad("inv.slot.selected"); this.index = index; this.slots = allSlots; diff --git a/src/mightypork/rogue/screens/game/InvLayer.java b/src/mightypork/rogue/screens/game/InventoryLayer.java similarity index 92% rename from src/mightypork/rogue/screens/game/InvLayer.java rename to src/mightypork/rogue/screens/game/InventoryLayer.java index 7926d47..72b17ee 100644 --- a/src/mightypork/rogue/screens/game/InvLayer.java +++ b/src/mightypork/rogue/screens/game/InventoryLayer.java @@ -22,7 +22,7 @@ import mightypork.rogue.world.item.Item; import mightypork.rogue.world.item.ItemType; -public class InvLayer extends ScreenLayer { +public class InventoryLayer extends ScreenLayer { private static final int SLOT_COUNT = 8; private static final int SLOT_ROW = 4; @@ -86,7 +86,7 @@ public class InvLayer extends ScreenLayer { } - public InvLayer(final ScreenGame screen) + public InventoryLayer(final ScreenGame screen) { super(screen); @@ -200,17 +200,7 @@ public class InvLayer extends ScreenLayer { final int selected = getSelectedSlot(); if (selected != -1) { - final PlayerFacade pl = WorldProvider.get().getPlayer(); - final Item itm = pl.getInventory().getItem(selected); - if (itm != null && !itm.isEmpty()) { - - final Item piece = itm.split(1); - if (itm.isEmpty()) pl.getInventory().setItem(selected, null); - - if (!pl.getLevel().getTile(pl.getCoord()).dropItem(piece)) { - pl.getInventory().addItem(piece); // add back - } - } + WorldProvider.get().getPlayer().dropItem(selected); } } }); diff --git a/src/mightypork/rogue/screens/game/NavButton.java b/src/mightypork/rogue/screens/game/NavButton.java index e1c7aed..cd14946 100644 --- a/src/mightypork/rogue/screens/game/NavButton.java +++ b/src/mightypork/rogue/screens/game/NavButton.java @@ -20,9 +20,9 @@ public class NavButton extends ClickableComponent { public NavButton(TxQuad fg) { super(); - this.base = Res.txq("nav.button.bg.base"); - this.hover = Res.txq("nav.button.bg.hover"); - this.down = Res.txq("nav.button.bg.down"); + this.base = Res.getTxQuad("nav.button.bg.base"); + this.hover = Res.getTxQuad("nav.button.bg.hover"); + this.down = Res.getTxQuad("nav.button.bg.down"); this.fg = fg; } diff --git a/src/mightypork/rogue/screens/game/ScreenGame.java b/src/mightypork/rogue/screens/game/ScreenGame.java index 507fe35..6a9e893 100644 --- a/src/mightypork/rogue/screens/game/ScreenGame.java +++ b/src/mightypork/rogue/screens/game/ScreenGame.java @@ -10,7 +10,6 @@ import mightypork.gamecore.gui.screens.LayeredScreen; import mightypork.gamecore.input.KeyStroke; import mightypork.gamecore.input.Keys; import mightypork.gamecore.logging.Log; -import mightypork.gamecore.util.math.Calc; import mightypork.rogue.Config; import mightypork.rogue.GameStateManager.GameState; import mightypork.rogue.events.GameStateRequest; @@ -32,7 +31,7 @@ public class ScreenGame extends LayeredScreen { WORLD, INV; } - private InvLayer invLayer; + private InventoryLayer invLayer; private HudLayer hudLayer; private WorldLayer worldLayer; @@ -131,6 +130,17 @@ public class ScreenGame extends LayeredScreen { } }; + public Action actionDropLastPickedItem = new Action() { + + @Override + public void execute() + { + final PlayerFacade pl = WorldProvider.get().getPlayer(); + if (pl.isDead() || pl.getWorld().isPaused()) return; + pl.dropItem(pl.getInventory().getLastAddIndex()); + } + }; + /** * Set gui state (overlay) @@ -175,7 +185,7 @@ public class ScreenGame extends LayeredScreen { { super(app); - addLayer(invLayer = new InvLayer(this)); + addLayer(invLayer = new InventoryLayer(this)); invLayer.setEnabled(false); invLayer.setVisible(false); @@ -187,29 +197,18 @@ public class ScreenGame extends LayeredScreen { worldLayer.setEnabled(true); worldLayer.setVisible(true); - // TODO temporary, remove - bindKey(new KeyStroke(Keys.N, Keys.MOD_CONTROL), new Runnable() { - - @Override - public void run() - { - WorldProvider.get().createWorld(Calc.rand.nextLong()); - } - }); - //pause key bindKey(new KeyStroke(Keys.P), actionTogglePause); bindKey(new KeyStroke(Keys.PAUSE), actionTogglePause); bindKey(new KeyStroke(Keys.SPACE), actionTogglePause); bindKey(new KeyStroke(Keys.I), actionToggleInv); + bindKey(new KeyStroke(Keys.D), actionDropLastPickedItem); bindKey(new KeyStroke(Keys.E), actionEat); bindKey(new KeyStroke(Keys.M), actionToggleMinimap); bindKey(new KeyStroke(Keys.Z), actionToggleZoom); - bindKey(new KeyStroke(Keys.R, Keys.MOD_CONTROL), actionLoad); bindKey(new KeyStroke(Keys.L, Keys.MOD_CONTROL), actionLoad); - bindKey(new KeyStroke(Keys.S, Keys.MOD_CONTROL), actionSave); // add as actions - enableables. @@ -224,11 +223,12 @@ public class ScreenGame extends LayeredScreen { worldActions.add(actionSave); worldActions.add(actionLoad); worldActions.add(actionMenu); + worldActions.add(actionDropLastPickedItem); worldActions.setEnabled(true); - // TMP TODO remove - bindKey(new KeyStroke(Keys.X), new Runnable() { + // CHEAT - X-ray + bindKey(new KeyStroke(Keys.F10, Keys.MOD_CONTROL), new Runnable() { @Override public void run() diff --git a/src/mightypork/rogue/screens/game/WorldConsoleRenderer.java b/src/mightypork/rogue/screens/game/WorldConsoleRenderer.java index a9e1e1f..b606cdd 100644 --- a/src/mightypork/rogue/screens/game/WorldConsoleRenderer.java +++ b/src/mightypork/rogue/screens/game/WorldConsoleRenderer.java @@ -69,7 +69,7 @@ public class WorldConsoleRenderer extends BaseComponent { } } catch (final ConcurrentModificationException e) { - Log.e(e); // this should not happen anymore + Log.e(e); // this should not happen } diff --git a/src/mightypork/rogue/screens/menu/ScreenMainMenu.java b/src/mightypork/rogue/screens/menu/ScreenMainMenu.java index d2dacd6..bea06b2 100644 --- a/src/mightypork/rogue/screens/menu/ScreenMainMenu.java +++ b/src/mightypork/rogue/screens/menu/ScreenMainMenu.java @@ -64,7 +64,7 @@ public class ScreenMainMenu extends LayeredScreen { root.add(layout); int r = 0; - final ImagePainter ip = new ImagePainter(Res.txq("logo")); + final ImagePainter ip = new ImagePainter(Res.getTxQuad("logo")); ip.keepAspectRatio(); layout.put(ip, r, 0, 4, 1); r += 5; diff --git a/src/mightypork/rogue/screens/select_world/WorldSlot.java b/src/mightypork/rogue/screens/select_world/WorldSlot.java index 765debb..9d62ad2 100644 --- a/src/mightypork/rogue/screens/select_world/WorldSlot.java +++ b/src/mightypork/rogue/screens/select_world/WorldSlot.java @@ -162,6 +162,7 @@ public class WorldSlot extends ConstraintLayout { if (!file.exists()) { label = ""; + worldBundle = null; } else { try { worldBundle = Ion.fromFile(file); @@ -173,7 +174,8 @@ public class WorldSlot extends ConstraintLayout { delBtn.setVisible(true); delBtn.setEnabled(true); - } catch (final IOException e) { + } catch (final Exception e) { + Log.w("Error loading world save.", e); label = ""; } } diff --git a/src/mightypork/rogue/world/Inventory.java b/src/mightypork/rogue/world/Inventory.java index a5e8586..4f21302 100644 --- a/src/mightypork/rogue/world/Inventory.java +++ b/src/mightypork/rogue/world/Inventory.java @@ -14,6 +14,7 @@ public class Inventory implements IonObjBinary { public static final short ION_MARK = 54; private Item[] items; + private int lastAddIndex = 0; public Inventory(int size) @@ -87,6 +88,8 @@ public class Inventory implements IonObjBinary { */ public Item getItem(int i) { + if (i < 0 || i > getSize()) return null; + verifyIndex(i); final Item itm = items[i]; if (itm == null || itm.isEmpty()) return null; @@ -112,6 +115,7 @@ public class Inventory implements IonObjBinary { { verifyIndex(i); items[i] = item; + lastAddIndex = i; } @@ -136,7 +140,10 @@ public class Inventory implements IonObjBinary { for (int i = 0; i < getSize(); i++) { final Item itm = getItem(i); if (itm != null) { - if (itm.addItem(stored)) return true; + if (itm.addItem(stored)) { + lastAddIndex = i; + return true; + } } } @@ -145,6 +152,7 @@ public class Inventory implements IonObjBinary { final Item itm = getItem(i); if (itm == null) { setItem(i, stored.split(stored.getAmount())); // store a copy, empty the original item. + lastAddIndex = i; return true; } } @@ -167,6 +175,12 @@ public class Inventory implements IonObjBinary { } + public int getLastAddIndex() + { + return lastAddIndex; + } + + @Override public String toString() { diff --git a/src/mightypork/rogue/world/PlayerControl.java b/src/mightypork/rogue/world/PlayerControl.java index 56c0791..9a72397 100644 --- a/src/mightypork/rogue/world/PlayerControl.java +++ b/src/mightypork/rogue/world/PlayerControl.java @@ -5,7 +5,7 @@ import java.util.HashSet; import java.util.Set; import mightypork.gamecore.util.math.algo.Coord; -import mightypork.gamecore.util.math.algo.Step; +import mightypork.gamecore.util.math.algo.Move; import mightypork.gamecore.util.math.constraints.vect.Vect; import mightypork.rogue.world.entity.Entity; import mightypork.rogue.world.entity.EntityType; @@ -50,25 +50,25 @@ public abstract class PlayerControl { public void goNorth() { - go(Step.NORTH); + go(Move.NORTH); } public void goSouth() { - go(Step.SOUTH); + go(Move.SOUTH); } public void goEast() { - go(Step.EAST); + go(Move.EAST); } public void goWest() { - go(Step.WEST); + go(Move.WEST); } @@ -85,7 +85,7 @@ public abstract class PlayerControl { } - public boolean canGo(Step side) + public boolean canGo(Move side) { return getPlayer().canGoTo(side); } @@ -97,7 +97,7 @@ public abstract class PlayerControl { * @param side * @return */ - public boolean clickTile(Step side) + public boolean clickTile(Move side) { return doClickTile(getPlayer().getCoord().add(side).toVect()); } @@ -132,14 +132,14 @@ public abstract class PlayerControl { } - public void go(Step side) + public void go(Move side) { getPlayer().cancelPath(); getPlayer().addPathStep(side); } - public boolean tryGo(Step e) + public boolean tryGo(Move e) { if (!canGo(e)) return false; go(e); diff --git a/src/mightypork/rogue/world/PlayerFacade.java b/src/mightypork/rogue/world/PlayerFacade.java index 85bc7d9..7c48313 100644 --- a/src/mightypork/rogue/world/PlayerFacade.java +++ b/src/mightypork/rogue/world/PlayerFacade.java @@ -7,7 +7,7 @@ import java.util.Comparator; import java.util.List; import mightypork.gamecore.util.math.algo.Coord; -import mightypork.gamecore.util.math.algo.Step; +import mightypork.gamecore.util.math.algo.Move; import mightypork.gamecore.util.math.constraints.vect.Vect; import mightypork.rogue.world.entity.Entity; import mightypork.rogue.world.item.Item; @@ -158,7 +158,7 @@ public class PlayerFacade { } - public void addPathStep(Step step) + public void addPathStep(Move step) { world.playerEntity.pos.addStep(step); } @@ -400,8 +400,32 @@ public class PlayerFacade { } - public boolean canGoTo(Step side) + public boolean canGoTo(Move side) { return getEntity().pos.canGoTo(side); } + + + public void dropItem(int itemIndex) + { + final Item itm = getInventory().getItem(itemIndex); + if (itm != null && !itm.isEmpty()) { + + final Item piece = itm.split(1); + getInventory().clean(); + + Coord dropPos; + if (world.playerEntity.pos.isMoving()) { + dropPos = world.playerEntity.pos.getLastPos(); + } else { + dropPos = getCoord(); + } + + if (!getLevel().getTile(dropPos).dropItem(piece)) { + getInventory().addItem(piece); // add back + } else { + world.getConsole().msgDroppedItem(piece); + } + } + } } diff --git a/src/mightypork/rogue/world/WorldConsole.java b/src/mightypork/rogue/world/WorldConsole.java index 17e01d3..73903a2 100644 --- a/src/mightypork/rogue/world/WorldConsole.java +++ b/src/mightypork/rogue/world/WorldConsole.java @@ -129,7 +129,7 @@ public class WorldConsole implements Updateable { public void msgDie(Entity attacker) { - addMessage("You've been defeated by a " + attacker.getVisualName() + "!"); + addMessage("You've been defeated by " + addArticle(attacker.getVisualName()) + "!"); } @@ -141,7 +141,7 @@ public class WorldConsole implements Updateable { public void msgEat(Item item) { - addMessage("You've eaten a " + item.getVisualName() + "."); + addMessage("You've eaten " + addArticle(item.getVisualName()) + "."); } @@ -153,7 +153,7 @@ public class WorldConsole implements Updateable { public void msgEquipWeapon(Item item) { - addMessage("You're now wielding " + (item == null ? "NOTHING" : "a " + item.getVisualName()) + "."); + addMessage("You're now wielding " + (item == null ? "NOTHING" : addArticle(item.getVisualName())) + "."); } @@ -165,7 +165,7 @@ public class WorldConsole implements Updateable { public void msgKill(Entity prey) { - addMessage("You've killed a " + prey.getVisualName() + "."); + addMessage("You've killed " + addArticle(prey.getVisualName()) + "."); } @@ -183,12 +183,19 @@ public class WorldConsole implements Updateable { public void msgPick(Item item) { - addMessage("You've picked up a " + item.getVisualName() + "."); + addMessage("You've picked up " + addArticle(item.getVisualName()) + "."); lastPickupItem = item; timeSinceLastPickup = 0; } + public void msgDroppedItem(Item item) + { + addMessage("You've dropped " + addArticle(item.getVisualName()) + "."); + lastPickupItem = null; + } + + public void msgWeaponBreak(Item item) { addMessage("Your " + item.getVisualName() + " has broken!"); @@ -217,4 +224,26 @@ public class WorldConsole implements Updateable { { addMessage("Error while loading; See the log for details."); } + + + public void msgOpenChest() + { + addMessage("You've opened a treasure chest!"); + } + + + private String addArticle(String name) + { + switch (Character.toLowerCase(name.charAt(0))) { + case 'a': + case 'e': + case 'i': + case 'o': + case 'u': + case 'y': + return "an " + name; + default: + return "a " + name; + } + } } diff --git a/src/mightypork/rogue/world/entity/EntityPathFinder.java b/src/mightypork/rogue/world/entity/EntityPathFinder.java index 03e238c..af456c4 100644 --- a/src/mightypork/rogue/world/entity/EntityPathFinder.java +++ b/src/mightypork/rogue/world/entity/EntityPathFinder.java @@ -1,9 +1,11 @@ package mightypork.rogue.world.entity; +import java.util.List; + import mightypork.gamecore.util.math.algo.Coord; -import mightypork.gamecore.util.math.algo.Sides; -import mightypork.gamecore.util.math.algo.Step; +import mightypork.gamecore.util.math.algo.Move; +import mightypork.gamecore.util.math.algo.Moves; import mightypork.gamecore.util.math.algo.pathfinding.Heuristic; import mightypork.gamecore.util.math.algo.pathfinding.PathFinder; @@ -53,9 +55,9 @@ public class EntityPathFinder extends PathFinder { @Override - public Step[] getWalkSides() + public List getWalkSides() { - return Sides.CARDINAL_SIDES; + return Moves.CARDINAL_SIDES; } } diff --git a/src/mightypork/rogue/world/entity/impl/BossRatAi.java b/src/mightypork/rogue/world/entity/impl/BossRatAi.java index fc1fb54..8b54ba2 100644 --- a/src/mightypork/rogue/world/entity/impl/BossRatAi.java +++ b/src/mightypork/rogue/world/entity/impl/BossRatAi.java @@ -22,14 +22,14 @@ public class BossRatAi extends GrayRatAi { { super(entity); - setAttackTime(0.7); + setAttackTime(0.2); } @Override protected int getAttackStrength() { - return Calc.randInt(5, 11); + return Calc.randInt(1, 6); } @@ -52,6 +52,6 @@ public class BossRatAi extends GrayRatAi { @Override protected double getStepTime() { - return isIdle() ? 0.6 : 0.4; + return isIdle() ? 0.6 : 0.37; } } diff --git a/src/mightypork/rogue/world/entity/impl/MonsterAi.java b/src/mightypork/rogue/world/entity/impl/MonsterAi.java index 133c102..27d6402 100644 --- a/src/mightypork/rogue/world/entity/impl/MonsterAi.java +++ b/src/mightypork/rogue/world/entity/impl/MonsterAi.java @@ -8,8 +8,8 @@ import mightypork.gamecore.util.annot.DefaultImpl; import mightypork.gamecore.util.ion.IonBundle; import mightypork.gamecore.util.math.Calc; import mightypork.gamecore.util.math.algo.Coord; -import mightypork.gamecore.util.math.algo.Sides; -import mightypork.gamecore.util.math.algo.Step; +import mightypork.gamecore.util.math.algo.Move; +import mightypork.gamecore.util.math.algo.Moves; import mightypork.gamecore.util.math.algo.pathfinding.PathFinder; import mightypork.gamecore.util.math.algo.pathfinding.PathFinderProxy; import mightypork.rogue.world.entity.AiTimer; @@ -65,7 +65,7 @@ public class MonsterAi extends EntityModule implements EntityMoveListener { if (entity.pos.isMoving()) return; if (Calc.rand.nextInt(10) == 0) { - entity.pos.addStep(Sides.randomCardinal()); + entity.pos.addStep(Moves.randomCardinal()); } } }; @@ -260,7 +260,7 @@ public class MonsterAi extends EntityModule implements EntityMoveListener { } - private List getPathToPrey(Entity prey) + private List getPathToPrey(Entity prey) { if (!isPreyValid(prey)) return null; @@ -280,7 +280,7 @@ public class MonsterAi extends EntityModule implements EntityMoveListener { return; } - final List preyPath = getPathToPrey(prey); + final List preyPath = getPathToPrey(prey); if (preyPath == null || preyPath.size() > getPreyAbandonDistance()) { stopChasing(); diff --git a/src/mightypork/rogue/world/entity/modules/EntityModulePosition.java b/src/mightypork/rogue/world/entity/modules/EntityModulePosition.java index 7be52f3..1b396e5 100644 --- a/src/mightypork/rogue/world/entity/modules/EntityModulePosition.java +++ b/src/mightypork/rogue/world/entity/modules/EntityModulePosition.java @@ -10,7 +10,7 @@ import java.util.Set; import mightypork.gamecore.util.ion.IonBundle; import mightypork.gamecore.util.math.algo.Coord; -import mightypork.gamecore.util.math.algo.Step; +import mightypork.gamecore.util.math.algo.Move; import mightypork.gamecore.util.math.constraints.vect.VectConst; import mightypork.rogue.world.entity.Entity; import mightypork.rogue.world.entity.EntityModule; @@ -22,7 +22,7 @@ public class EntityModulePosition extends EntityModule { private final Coord lastPos = new Coord(0, 0); private boolean walking = false; - private final Queue path = new LinkedList<>(); + private final Queue path = new LinkedList<>(); private final EntityPos entityPos = new EntityPos(); private double stepTime = 0.5; @@ -136,7 +136,7 @@ public class EntityModulePosition extends EntityModule { walking = true; - final Step step = path.poll(); + final Move step = path.poll(); final Coord planned = entityPos.getCoord().add(step.toCoord()); @@ -177,7 +177,7 @@ public class EntityModulePosition extends EntityModule { * * @param step */ - public void addStep(Step step) + public void addStep(Move step) { if (path.isEmpty() && !canGoTo(step)) return; @@ -203,7 +203,7 @@ public class EntityModulePosition extends EntityModule { public boolean navigateTo(Coord target) { if (target.equals(getCoord())) return true; - final List newPath = entity.getPathFinder().findPathRelative(entityPos.getCoord(), target); + final List newPath = entity.getPathFinder().findPathRelative(entityPos.getCoord(), target); if (newPath == null) return false; cancelPath(); @@ -228,7 +228,7 @@ public class EntityModulePosition extends EntityModule { * * @param path steps */ - public void addSteps(List path) + public void addSteps(List path) { this.path.addAll(path); } @@ -284,9 +284,15 @@ public class EntityModulePosition extends EntityModule { } - public boolean canGoTo(Step side) + public boolean canGoTo(Move side) { return entity.getPathFinder().isAccessible(getCoord().add(side)); } + + public Coord getLastPos() + { + return lastPos; + } + } diff --git a/src/mightypork/rogue/world/entity/modules/EntityPos.java b/src/mightypork/rogue/world/entity/modules/EntityPos.java index 78c95ce..140cbf9 100644 --- a/src/mightypork/rogue/world/entity/modules/EntityPos.java +++ b/src/mightypork/rogue/world/entity/modules/EntityPos.java @@ -8,7 +8,7 @@ import mightypork.gamecore.util.ion.IonBundle; import mightypork.gamecore.util.ion.IonObjBundled; import mightypork.gamecore.util.math.Easing; import mightypork.gamecore.util.math.algo.Coord; -import mightypork.gamecore.util.math.algo.Step; +import mightypork.gamecore.util.math.algo.Move; import mightypork.gamecore.util.math.constraints.vect.Vect; import mightypork.gamecore.util.math.constraints.vect.VectConst; import mightypork.gamecore.util.math.constraints.vect.mutable.VectAnimated; @@ -128,7 +128,7 @@ class EntityPos implements IonObjBundled, Updateable { } - public void walk(Step step, double secs) + public void walk(Move step, double secs) { setTo(coord.x + step.x(), coord.y + step.y()); walkOffset.setTo(-step.x(), -step.y()); diff --git a/src/mightypork/rogue/world/entity/render/EntityRendererMobLR.java b/src/mightypork/rogue/world/entity/render/EntityRendererMobLR.java index b26884e..8c3d5bb 100644 --- a/src/mightypork/rogue/world/entity/render/EntityRendererMobLR.java +++ b/src/mightypork/rogue/world/entity/render/EntityRendererMobLR.java @@ -37,7 +37,7 @@ public class EntityRendererMobLR extends EntityRenderer { public EntityRendererMobLR(Entity entity, String sheetKey) { this.entity = entity; - this.sheet = Res.txs(sheetKey); + this.sheet = Res.getTxSheet(sheetKey); } diff --git a/src/mightypork/rogue/world/gen/LevelBuilder.java b/src/mightypork/rogue/world/gen/LevelBuilder.java new file mode 100644 index 0000000..00c51e7 --- /dev/null +++ b/src/mightypork/rogue/world/gen/LevelBuilder.java @@ -0,0 +1,253 @@ +package mightypork.rogue.world.gen; + + +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; + +import mightypork.gamecore.util.math.Range; +import mightypork.gamecore.util.math.algo.Coord; +import mightypork.rogue.world.World; +import mightypork.rogue.world.entity.Entity; +import mightypork.rogue.world.item.Item; +import mightypork.rogue.world.level.Level; + + +public class LevelBuilder { + + public static enum BuildOrder + { + FIRST, MIDDLE, LAST + } + + private class RoomEntry { + + int count; + RoomBuilder room; + boolean important; + + + public RoomEntry(RoomBuilder room, int count, boolean important) + { + this.count = count; + this.room = room; + this.important = important; + } + } + + private class ItemEntry { + + Item item; + boolean important; + + + public ItemEntry(Item item, boolean important) + { + this.item = item; + this.important = important; + } + } + + private class EntityEntry { + + Entity entity; + boolean important; + + + public EntityEntry(Entity item, boolean important) + { + this.entity = item; + this.important = important; + } + } + + private final ScratchMap map; + private final Random rand; + private boolean built; + + private final LinkedList roomsFirst = new LinkedList<>(); + private final LinkedList roomsMiddle = new LinkedList<>(); + private final LinkedList roomsLast = new LinkedList<>(); + + private final LinkedList items = new LinkedList<>(); + private final LinkedList entities = new LinkedList<>(); + + + /** + * make a new level builder instance. + * + * @param max_size max map size (square side - tiles) + * @param theme tiles theme + * @param seed level seed + */ + public LevelBuilder(int max_size, MapTheme theme, long seed) + { + this.rand = new Random(seed); + this.map = new ScratchMap(max_size, theme, rand); + } + + + /** + * Add a single room to the room buffer. + * + * @param room room builder + * @param order build order + * @param important try harder and throw error on fail + */ + public void addRoom(RoomBuilder room, BuildOrder order, boolean important) + { + addRoom(room, Range.make(1, 1), order, important); + } + + + /** + * Add multiple rooms of the type to the room buffer. + * + * @param room room builder + * @param count number of rooms to build + * @param order build order + * @param important try harder and throw error on fail + */ + public void addRoom(RoomBuilder room, Range count, BuildOrder order, boolean important) + { + final List list; + + switch (order) { + case FIRST: + list = roomsFirst; + break; + + default: + case MIDDLE: + list = roomsMiddle; + break; + + case LAST: + list = roomsLast; + break; + } + + list.add(new RoomEntry(room, count.randInt(rand), important)); + } + + + private void buildRooms(LinkedList list) + { + while (!list.isEmpty()) { + + Collections.shuffle(list, rand); + + for (final Iterator iter = list.iterator(); iter.hasNext();) { + final RoomEntry rge = iter.next(); + + map.addRoom(rge.room, rge.important); + + if ((--rge.count) <= 0) { + iter.remove(); + } + } + } + } + + + private void buildCorridors() throws WorldGenError + { + map.buildCorridors(); + } + + + private void buildEntities() + { + for (final EntityEntry entry : entities) { + final int tries = entry.important ? 200 : 50; + final boolean success = map.addEntityInMap(entry.entity, tries); + + if (entry.important && !success) { + throw new WorldGenError("Could not place an important entity: " + entry.entity); + } + } + } + + + private void buildItems() + { + for (final ItemEntry entry : items) { + final int tries = entry.important ? 200 : 50; + final boolean success = map.addItemInMap(entry.item, tries); + + if (entry.important && !success) { + throw new WorldGenError("Could not place an important item: " + entry.item); + } + } + } + + + private void writeToMap() + { + buildRooms(roomsFirst); + buildRooms(roomsMiddle); + buildRooms(roomsLast); + buildCorridors(); + + map.fixGlitches(); + + buildItems(); + buildEntities(); + } + + + /** + * Write to a new level instance. + * + * @param world level's world + * @return the level + * @throws WorldGenError on error in generation + */ + public Level build(World world) throws WorldGenError + { + if (built) { + throw new WorldGenError("Level already built."); + } + built = true; + + writeToMap(); + + final Coord size = map.getNeededSize(); + final Level lvl = new Level(size.x, size.y); + lvl.setWorld(world); // important for creating entities + + map.writeToLevel(lvl); + + return lvl; + } + + + /** + * Add an item to be added to the level when tiles are built. + * + * @param item item to add + * @param important try harder and throw error on fail + * @throws WorldGenError on fail + */ + public void addItem(Item item, boolean important) throws WorldGenError + { + items.add(new ItemEntry(item, important)); + } + + + /** + * Add an entity to be added to the level when tiles are built.
+ * It's EID will be assigned during writing to level. + * + * @param entity entity to add + * @param important try harder and throw error on fail + * @throws WorldGenError on fail + */ + public void addEntity(Entity entity, boolean important) throws WorldGenError + { + entities.add(new EntityEntry(entity, important)); + } + +} diff --git a/src/mightypork/rogue/world/gen/LevelGenerator.java b/src/mightypork/rogue/world/gen/LevelGenerator.java deleted file mode 100644 index 81b3bec..0000000 --- a/src/mightypork/rogue/world/gen/LevelGenerator.java +++ /dev/null @@ -1,124 +0,0 @@ -package mightypork.rogue.world.gen; - - -import java.util.Random; - -import mightypork.gamecore.logging.Log; -import mightypork.gamecore.util.math.Calc; -import mightypork.gamecore.util.math.algo.Coord; -import mightypork.rogue.world.World; -import mightypork.rogue.world.entity.Entities; -import mightypork.rogue.world.entity.Entity; -import mightypork.rogue.world.gen.rooms.Rooms; -import mightypork.rogue.world.gen.themes.ThemeBrick; -import mightypork.rogue.world.item.Items; -import mightypork.rogue.world.level.Level; - - -public class LevelGenerator { - - public static final MapTheme DUNGEON_THEME = new ThemeBrick(); - - - @SuppressWarnings("fallthrough") - public static Level build(World world, long seed, int level, MapTheme theme, boolean lastLevel) throws WorldGenError - { - Log.f3("Generating level of complexity: " + level); - - final Random rand = new Random(seed + 13); - - final int max_size = 128; - - final ScratchMap map = new ScratchMap(max_size, theme, rand); - - // start - if (!map.addRoom(Rooms.ENTRANCE, true)) { - throw new WorldGenError("Could not place entrance room."); - } - - for (int i = 0; i < Calc.randInt(rand, 1 + level, (int) (1 + level * 1.5)); i++) { - map.addRoom(Rooms.BASIC, false); - - // spice it up with dead ends - if (rand.nextInt(6) > 0) map.addRoom(Rooms.DEAD_END, false); - } - - for (int i = 0; i < Calc.randInt(rand, 1, (int) Math.ceil(level / 2D)); i++) { - map.addRoom(Rooms.TREASURE, false); - } - - if (!lastLevel) { - if (!map.addRoom(Rooms.EXIT, true)) { - throw new WorldGenError("Could not place exit room."); - } - } - - if (lastLevel) { - if (!map.addRoom(Rooms.BOSS, true)) { - throw new WorldGenError("Could not place boss room."); - } - } - - map.addRoom(Rooms.HEART_ROOM, true); - - - map.buildCorridors(); - - switch (level) { - default: - case 3: - case 2: - if (rand.nextInt(2) == 0) map.putItemInMap(Items.CLUB.createItemDamaged(30), 50); - case 1: - if (rand.nextInt(2) == 0) map.putItemInMap(Items.ROCK.createItemDamaged(10), 50); - } - - if (level == 1) { - map.putItemInMap(Items.BONE.createItemDamaged(20), 60); - } - - if (level == 2) { - map.putItemInMap(Items.CLUB.createItemDamaged(50), 60); - } - - if (level == 6) { - map.putItemInMap(Items.SWORD.createItemDamaged(60), 200); - } - - if (level == 4) { - map.putItemInMap(Items.HAMMER.createItemDamaged(40), 100); - } - - // entities - random rats - - - for (int i = 0; i < Calc.randInt(rand, 2 + level * 2, 5 + level * 3); i++) { - Entity e; - - if (level > 2 && rand.nextInt(level - 2 + 1) != 0) { - e = Entities.RAT_BROWN.createEntity(); - } else { - e = Entities.RAT_GRAY.createEntity(); - } - - map.putEntityInMap(e, 30); - - if (rand.nextInt(6 + level / 2) == 0) { - map.putItemInMap(Items.CHEESE.createItem(), 10); - } - - if (rand.nextInt(6) == 0) { - map.putItemInMap(Items.MEAT.createItem(), 10); - } - } - - - final Coord size = map.getNeededSize(); - final Level lvl = new Level(size.x, size.y); - lvl.setWorld(world); // important for creating entities - - map.writeToLevel(lvl); - - return lvl; - } -} diff --git a/src/mightypork/rogue/world/gen/MapTheme.java b/src/mightypork/rogue/world/gen/MapTheme.java index d4be91c..22fbf0b 100644 --- a/src/mightypork/rogue/world/gen/MapTheme.java +++ b/src/mightypork/rogue/world/gen/MapTheme.java @@ -30,4 +30,7 @@ public interface MapTheme { TileModel exit(); + + + TileModel chest(); } diff --git a/src/mightypork/rogue/world/gen/RoomBuilder.java b/src/mightypork/rogue/world/gen/RoomBuilder.java index 39c3e4d..7da8a82 100644 --- a/src/mightypork/rogue/world/gen/RoomBuilder.java +++ b/src/mightypork/rogue/world/gen/RoomBuilder.java @@ -13,5 +13,5 @@ import mightypork.gamecore.util.math.algo.Coord; */ public interface RoomBuilder { - RoomDesc buildToFit(ScratchMap map, MapTheme theme, Random rand, Coord center) throws WorldGenError; + RoomEntry buildRoom(ScratchMap map, MapTheme theme, Random rand, Coord center) throws WorldGenError; } diff --git a/src/mightypork/rogue/world/gen/RoomDesc.java b/src/mightypork/rogue/world/gen/RoomEntry.java similarity index 88% rename from src/mightypork/rogue/world/gen/RoomDesc.java rename to src/mightypork/rogue/world/gen/RoomEntry.java index e3dc346..56a43fc 100644 --- a/src/mightypork/rogue/world/gen/RoomDesc.java +++ b/src/mightypork/rogue/world/gen/RoomEntry.java @@ -5,17 +5,17 @@ import mightypork.gamecore.util.math.algo.Coord; /** - * Room description + * Room description entry for {@link ScratchMap} * * @author MightyPork */ -public class RoomDesc { +public class RoomEntry { final Coord min; final Coord max; - public RoomDesc(Coord min, Coord max) + public RoomEntry(Coord min, Coord max) { super(); this.min = min; diff --git a/src/mightypork/rogue/world/gen/Rooms.java b/src/mightypork/rogue/world/gen/Rooms.java new file mode 100644 index 0000000..86a399a --- /dev/null +++ b/src/mightypork/rogue/world/gen/Rooms.java @@ -0,0 +1,28 @@ +package mightypork.rogue.world.gen; + + +import mightypork.rogue.world.gen.rooms.*; +import mightypork.rogue.world.item.Item; + + +public class Rooms { + + public static final RoomBuilder BASIC = new BasicRoom(); + public static final RoomBuilder STORAGE = new StorageRoom(); + public static final RoomBuilder DEAD_END = new DeadEndRoom(); + public static final RoomBuilder ENTRANCE = new EntranceRoom(); + public static final RoomBuilder EXIT = new ExitRoom(); + public static final RoomBuilder BOSS = new BossRoom(); + + + public static RoomBuilder treasure(Item item) + { + return new TreasureChestRoom(item); + } + + + public static RoomBuilder shrine(Item item) + { + return new ItemShrineRoom(item); + } +} diff --git a/src/mightypork/rogue/world/gen/ScratchMap.java b/src/mightypork/rogue/world/gen/ScratchMap.java index 733781e..0d96027 100644 --- a/src/mightypork/rogue/world/gen/ScratchMap.java +++ b/src/mightypork/rogue/world/gen/ScratchMap.java @@ -10,8 +10,8 @@ import java.util.Set; import mightypork.gamecore.logging.Log; import mightypork.gamecore.util.math.Calc; import mightypork.gamecore.util.math.algo.Coord; -import mightypork.gamecore.util.math.algo.Sides; -import mightypork.gamecore.util.math.algo.Step; +import mightypork.gamecore.util.math.algo.Move; +import mightypork.gamecore.util.math.algo.Moves; import mightypork.gamecore.util.math.algo.pathfinding.Heuristic; import mightypork.gamecore.util.math.algo.pathfinding.PathFinder; import mightypork.rogue.world.entity.Entity; @@ -33,7 +33,7 @@ public class ScratchMap { private final int width; private final int height; - private final List rooms = new ArrayList<>(); + private final List rooms = new ArrayList<>(); /** Coords to connect with corridors */ private final List nodes = new ArrayList<>(); @@ -96,9 +96,9 @@ public class ScratchMap { @Override - public Step[] getWalkSides() + public List getWalkSides() { - return Sides.CARDINAL_SIDES; + return Moves.CARDINAL_SIDES; } }; @@ -136,83 +136,99 @@ public class ScratchMap { } - public boolean addRoom(RoomBuilder rb, boolean critical) + public void addRoom(RoomBuilder rb, boolean critical) throws WorldGenError { - final Coord center = Coord.make(0, 0); - - int failed = 0; - - int failed_total = 0; - - while (true) { + try { + if (rooms.size() > 0) minimizeBounds(); - final int sizeX = genMax.x - genMin.x; - final int sizeY = genMax.y - genMin.y; + final Coord roomPos = Coord.make(0, 0); - center.x = genMin.x + rand.nextInt(sizeX); - center.y = genMin.y + rand.nextInt(sizeY); + int failed = 0; + int failed_total = 0; - switch (rand.nextInt(4)) { - case 0: - center.x += (failed_total / 35); - break; - case 1: - center.x -= (failed_total / 35); - break; - case 2: - center.y += (failed_total / 35); - break; - case 3: - center.y -= (failed_total / 35); - } - - final RoomDesc rd = rb.buildToFit(this, theme, rand, center); - if (rd != null) { - rooms.add(rd); - - genMin.x = Math.min(genMin.x, rd.min.x); - genMin.y = Math.min(genMin.y, rd.min.y); + while (true) { - genMax.x = Math.max(genMax.x, rd.max.x); - genMax.y = Math.max(genMax.y, rd.max.y); - clampBounds(); + final int sizeX = genMax.x - genMin.x; + final int sizeY = genMax.y - genMin.y; - nodes.add(center); -// Log.f3("Placed room (failed " + failed_total + " x)."); + roomPos.x = genMin.x + rand.nextInt(sizeX + 1); + roomPos.y = genMin.y + rand.nextInt(sizeY + 1); - return true; - } else { - failed++; - failed_total++; - - if (failed_total > 1000) { - return false; + switch (rand.nextInt(4)) { + case 0: + roomPos.x += (failed_total / 35); + break; + case 1: + roomPos.x -= (failed_total / 35); + break; + case 2: + roomPos.y += (failed_total / 35); + break; + case 3: + roomPos.y -= (failed_total / 35); } - if (failed > 300) { - Log.w("Faild to build room."); - if (critical) { - - genMin.x -= 5; - genMin.y -= 5; - genMax.x += 5; - genMax.y += 5; - clampBounds(); - - failed = 0; - Log.f3("Trying again."); - continue; - - } else { - return false; + final RoomEntry rd = rb.buildRoom(this, theme, rand, roomPos); + if (rd != null) { + + rooms.add(rd); + + genMin.x = Math.min(genMin.x, rd.min.x); + genMin.y = Math.min(genMin.y, rd.min.y); + + genMax.x = Math.max(genMax.x, rd.max.x); + genMax.y = Math.max(genMax.y, rd.max.y); + clampBounds(); + + nodes.add(roomPos); + + return; + + } else { + failed++; + failed_total++; + + if (failed_total > 1000) { + throw new WorldGenError("Failed to add a room."); + } + + if (failed > 300) { + Log.w("Faild to build room."); + if (critical) { + + // expand gen bounds + genMin.x -= 5; + genMin.y -= 5; + genMax.x += 5; + genMax.y += 5; + + clampBounds(); + + failed = 0; + Log.f3("Trying again."); + continue; + + } else { + throw new WorldGenError("Failed to add a room."); + } } } } + } catch (final WorldGenError e) { + if (!critical) { + Log.w("Could not place a room.", e); + return; + } else { + // rethrow + throw e; + } } - } + /** + * Clamp bounds to available area + */ private void clampBounds() { genMin.x = Calc.clamp(genMin.x, 0, width - 1); @@ -222,6 +238,27 @@ public class ScratchMap { } + /** + * Minimize gen bounds based on defined room bounds + */ + private void minimizeBounds() + { + final Coord low = Coord.make(width, height); + final Coord high = Coord.make(0, 0); + + for (final RoomEntry rd : rooms) { + low.x = Math.min(low.x, rd.min.x); + low.y = Math.min(low.y, rd.min.y); + + high.x = Math.max(high.x, rd.max.x); + high.y = Math.max(high.y, rd.max.y); + } + + genMin.setTo(low); + genMax.setTo(high); + } + + public boolean isIn(Coord pos) { return pos.x >= 0 && pos.x < width && pos.y >= 0 && pos.y < height; @@ -258,7 +295,7 @@ public class ScratchMap { public boolean isClear(Coord min, Coord max) { if (!isIn(min) || !isIn(max)) return false; - for (final RoomDesc r : rooms) { + for (final RoomEntry r : rooms) { if (r.intersectsWith(min, max)) return false; } @@ -266,6 +303,12 @@ public class ScratchMap { } + public void protect(Coord pos, TileProtectLevel prot) + { + protect(pos, pos, prot); + } + + public void protect(Coord min, Coord max, TileProtectLevel prot) { if (!isIn(min) || !isIn(max)) throw new WorldGenError("Tile(s) not in map: " + min + " , " + max); @@ -327,7 +370,7 @@ public class ScratchMap { } } starts.add(start); - start = Calc.pick(nodes); + start = Calc.pick(rand, nodes); } } @@ -377,31 +420,24 @@ public class ScratchMap { } + /** + * @return dimensions of the area taken by non-null tiles + */ public Coord getNeededSize() { return Coord.make(genMax.x - genMin.x + 1, genMax.y - genMin.y + 1); } - public int countBits(byte b) - { - int c = 0; - for (int i = 0; i < 8; i++) { - c += (b >> i) & 1; - } - return c; - } - - public byte findWalls(Coord pos) { byte walls = 0; for (int i = 0; i < 8; i++) { - final Coord cc = pos.add(Sides.get(i)); + final Coord cc = pos.add(Moves.getSide(i)); if (!isIn(cc)) continue; if (getTile(cc).isWall()) { - walls |= Sides.bit(i); + walls |= Moves.getBit(i); } } return walls; @@ -412,11 +448,11 @@ public class ScratchMap { { byte floors = 0; for (int i = 0; i <= 7; i++) { - final Coord cc = pos.add(Sides.get(i)); + final Coord cc = pos.add(Moves.getSide(i)); if (!isIn(cc)) continue; if (getTile(cc).isFloor()) { - floors |= Sides.bit(i); + floors |= Moves.getBit(i); } } return floors; @@ -427,11 +463,11 @@ public class ScratchMap { { byte doors = 0; for (int i = 0; i <= 7; i++) { - final Coord cc = pos.add(Sides.get(i)); + final Coord cc = pos.add(Moves.getSide(i)); if (!isIn(cc)) continue; if (getTile(cc).isDoor()) { - doors |= Sides.bit(i); + doors |= Moves.getBit(i); } } return doors; @@ -442,105 +478,129 @@ public class ScratchMap { { byte nils = 0; for (int i = 0; i <= 7; i++) { - final Coord cc = pos.add(Sides.get(i)); + final Coord cc = pos.add(Moves.getSide(i)); if (!isIn(cc) || getTile(cc).isNull()) { - nils |= Sides.bit(i); + nils |= Moves.getBit(i); } } return nils; } - public void writeToLevel(Level level) + /** + * Fix generator glitches and reduce size to the actual used size + */ + public void fixGlitches() { - if (level.getWorld() == null) { - throw new WorldGenError("Level has no world assigned."); // need for entities - } + final Tile[][] out = new Tile[height][width]; - // make sure no walkable are at edges. - final Coord c = Coord.make(0, 0); - final Coord c1 = Coord.make(0, 0); + // bounds will be adjusted by the actual tiles in the map + genMin.x = width; + genMin.y = height; + genMax.x = 0; + genMax.y = 0; - if (FIX_GLITCHES) { - - final Tile[][] out = new Tile[height][width]; - - for (c.x = 0; c.x < width; c.x++) { - for (c.y = 0; c.y < height; c.y++) { - - final Tile t = getTile(c); - final boolean isNull = t.isNull(); - - final boolean isDoor = !isNull && t.isDoor(); - final boolean isFloor = !isNull && t.isFloor(); - final boolean isWall = !isNull && t.isWall(); + final Coord c = Coord.make(0, 0); + for (c.x = 0; c.x < width; c.x++) { + for (c.y = 0; c.y < height; c.y++) { + + final Tile t = getTile(c); + final boolean isNull = t.isNull(); + + final boolean isDoor = !isNull && t.isDoor(); + final boolean isFloor = !isNull && t.isFloor(); + final boolean isWall = !isNull && t.isWall(); + + // bitmasks + final byte walls = findWalls(c); + final byte nils = findNils(c); + final byte floors = findFloors(c); + + boolean toWall = false; + boolean toFloor = false; + boolean toNull = false; + + do { + if (isWall && floors == 0) { + toNull = true; + break; + } - // bitmasks - final byte walls = findWalls(c); - final byte nils = findNils(c); - final byte floors = findFloors(c); + if (isFloor && (nils & Moves.BITS_CARDINAL) != 0) { + toWall = true; // floor with adjacent cardinal null + break; + } - boolean toWall = false; - boolean toFloor = false; - boolean toNull = false; + if (isNull && (floors & Moves.BITS_DIAGONAL) != 0) { + toWall = true; // null with adjacent diagonal floor + break; + } - do { - if (isWall && floors == 0) { - toNull = true; + if (isDoor) { + + if (Calc.countBits((byte) (floors & Moves.BITS_CARDINAL)) < 2) { + toWall = true; break; } - if (isFloor && (nils & Sides.MASK_CARDINAL) != 0) { - toWall = true; // floor with adjacent cardinal null + if (Calc.countBits((byte) (walls & Moves.BITS_CARDINAL)) > 2) { + toWall = true; break; } - if (isNull && (floors & Sides.MASK_DIAGONAL) != 0) { - toWall = true; // null with adjacent diagonal floor + if (Calc.countBits((byte) (floors & Moves.BITS_CARDINAL)) > 2) { + toFloor = true; break; } - if (isDoor) { - - if (countBits((byte) (floors & Sides.MASK_CARDINAL)) < 2) { - toWall = true; - break; - } - - if (countBits((byte) (walls & Sides.MASK_CARDINAL)) > 2) { - toWall = true; - break; - } - - if (countBits((byte) (floors & Sides.MASK_CARDINAL)) > 2) { - toFloor = true; - break; - } - - if ((floors & Sides.NW_CORNER) == Sides.NW_CORNER) toWall = true; - if ((floors & Sides.NE_CORNER) == Sides.NE_CORNER) toWall = true; - if ((floors & Sides.SW_CORNER) == Sides.SW_CORNER) toWall = true; - if ((floors & Sides.SE_CORNER) == Sides.SE_CORNER) toWall = true; - - } - } while (false); - - if (toNull) { - out[c.y][c.x] = Tiles.NULL.createTile(); - } else if (toWall) { - out[c.y][c.x] = theme.wall().createTile(); - } else if (toFloor) { - out[c.y][c.x] = theme.floor().createTile(); - } else { - out[c.y][c.x] = map[c.y][c.x]; + if ((floors & Moves.BITS_NW_CORNER) == Moves.BITS_NW_CORNER) toWall = true; + if ((floors & Moves.BITS_NE_CORNER) == Moves.BITS_NE_CORNER) toWall = true; + if ((floors & Moves.BITS_SW_CORNER) == Moves.BITS_SW_CORNER) toWall = true; + if ((floors & Moves.BITS_SE_CORNER) == Moves.BITS_SE_CORNER) toWall = true; + } + } while (false); + + if (toNull) { + out[c.y][c.x] = Tiles.NULL.createTile(); + } else if (toWall) { + out[c.y][c.x] = theme.wall().createTile(); + } else if (toFloor) { + out[c.y][c.x] = theme.floor().createTile(); + } else { + out[c.y][c.x] = map[c.y][c.x]; + } + + if (!out[c.y][c.x].isNull()) { + genMin.x = Math.min(genMin.x, c.x); + genMin.y = Math.min(genMin.y, c.y); + + genMax.x = Math.max(genMax.x, c.x); + genMax.y = Math.max(genMax.y, c.y); } } - - map = out; } + map = out; + } + + + /** + * Write tiles and entities into a level + * + * @param level the level + */ + public void writeToLevel(Level level) + { + if (level.getWorld() == null) { + throw new WorldGenError("Level has no world assigned."); // need for entities + } + + // make sure no walkable are at edges. + final Coord c = Coord.make(0, 0); + final Coord c1 = Coord.make(0, 0); + for (c.x = genMin.x, c1.x = 0; c.x <= genMax.x; c.x++, c1.x++) { for (c.y = genMin.y, c1.y = 0; c.y <= genMax.y; c.y++, c1.y++) { level.setTile(c1, getTile(c)); @@ -582,60 +642,74 @@ public class ScratchMap { } - public boolean putItem(Item item, Coord pos) + public boolean addItem(Item item, Coord pos) + { + return addItem(item, pos, true); + } + + + public boolean addItem(Item item, Coord pos, boolean canStack) { if (!isIn(pos)) return false; final Tile t = getTile(pos); + if (!canStack && t.hasItem()) return false; if (t.dropItem(item)) return true; return false; } - public boolean putItemInArea(Item item, Coord min, Coord max, int tries) + public boolean addItemInArea(Item item, Coord min, Coord max, int tries) { final Coord pos = Coord.zero(); - for (int i = 0; i < tries; i++) { - pos.x = min.x + rand.nextInt(max.x - min.x); - pos.y = min.y + rand.nextInt(max.y - min.y); - if (putItem(item, pos)) return true; + for (int i = 0; i < tries / 2; i++) { + pos.x = Calc.randInt(rand, min.x, max.x); + pos.y = Calc.randInt(rand, min.y, max.y); + if (addItem(item, pos, false)) return true; + } + + for (int i = 0; i < tries - (tries / 2); i++) { + pos.x = Calc.randInt(rand, min.x, max.x); + pos.y = Calc.randInt(rand, min.y, max.y); + if (addItem(item, pos, true)) return true; } return false; } - public boolean putItemInMap(Item item, int tries) + public boolean addItemInMap(Item item, int tries) { - return putItemInArea(item, genMin, genMax, tries); + Log.f3("gen bounds: " + genMin + " -> " + genMax); + return addItemInArea(item, genMin, genMax, tries); } - public boolean putEntityInArea(Entity entity, Coord min, Coord max, int tries) + public boolean addEntityInArea(Entity entity, Coord min, Coord max, int tries) { final Coord pos = Coord.zero(); for (int i = 0; i < tries; i++) { - pos.x = min.x + rand.nextInt(max.x - min.x); - pos.y = min.y + rand.nextInt(max.y - min.y); + pos.x = Calc.randInt(rand, min.x, max.x); + pos.y = Calc.randInt(rand, min.y, max.y); if (!isIn(pos)) continue; - if (putEntity(entity, pos)) return true; + if (addEntity(entity, pos)) return true; } return false; } - public boolean putEntityInMap(Entity entity, int tries) + public boolean addEntityInMap(Entity entity, int tries) { - return putEntityInArea(entity, genMin, genMax, tries); + return addEntityInArea(entity, genMin, genMax, tries); } - public boolean putEntity(Entity entity, Coord pos) + public boolean addEntity(Entity entity, Coord pos) { if (!isIn(pos)) return false; diff --git a/src/mightypork/rogue/world/gen/WorldCreator.java b/src/mightypork/rogue/world/gen/WorldCreator.java index 9b2f0b8..dfac5cd 100644 --- a/src/mightypork/rogue/world/gen/WorldCreator.java +++ b/src/mightypork/rogue/world/gen/WorldCreator.java @@ -1,9 +1,21 @@ package mightypork.rogue.world.gen; +import java.util.ArrayList; +import java.util.List; import java.util.Random; +import mightypork.gamecore.logging.Log; +import mightypork.gamecore.util.math.Calc; +import mightypork.gamecore.util.math.Range; import mightypork.rogue.world.World; +import mightypork.rogue.world.entity.Entities; +import mightypork.rogue.world.entity.Entity; +import mightypork.rogue.world.gen.LevelBuilder.BuildOrder; +import mightypork.rogue.world.gen.themes.ThemeBrick; +import mightypork.rogue.world.item.Item; +import mightypork.rogue.world.item.ItemModel; +import mightypork.rogue.world.item.Items; public class WorldCreator { @@ -15,20 +27,127 @@ public class WorldCreator { { synchronized (rand) { rand.setSeed(seed); + final MapTheme theme = new ThemeBrick(); final World w = new World(); w.setSeed(seed); - final int count = 7; + final LevelBuilder levelBuilders[] = new LevelBuilder[7]; - for (int lvl = 1; lvl <= count; lvl++) { - w.addLevel(LevelGenerator.build(w, rand.nextLong(), lvl, LevelGenerator.DUNGEON_THEME, lvl == count)); + // build the level rooms + for (int floor = 1; floor <= 7; floor++) { + final LevelBuilder lb = prepareFloor(rand.nextLong(), floor, theme, floor == 7); + + levelBuilders[floor - 1] = lb; } + + final List weaponsBasic = new ArrayList<>(); + weaponsBasic.add(Items.ROCK); + weaponsBasic.add(Items.BONE); + weaponsBasic.add(Items.TWIG); + + + final List weaponsMedium = new ArrayList<>(); + weaponsMedium.add(Items.CLUB); + + + final List weaponsGood = new ArrayList<>(); + weaponsGood.add(Items.AXE); + weaponsGood.add(Items.SWORD); + weaponsBasic.add(Items.KNIFE); + + + for (int i = 0; i < Calc.randInt(rand, 10, 15); i++) { + final Item item = Calc.pick(rand, weaponsBasic).createItemDamaged(50); + final LevelBuilder lb = levelBuilders[-1 + Calc.randInt(1, 7)]; + lb.addItem(item, false); + } + + + for (int i = 0; i < Calc.randInt(rand, 1, 3); i++) { + final Item item = Calc.pick(rand, weaponsMedium).createItemDamaged(60); + final LevelBuilder lb = levelBuilders[-1 + Calc.randInt(1, 3)]; + lb.addItem(item, false); + } + + + for (int i = 0; i < Calc.randInt(rand, 2, 3); i++) { + final Item item = Calc.pick(rand, weaponsGood).createItemDamaged(60); + final LevelBuilder lb = levelBuilders[-1 + Calc.randInt(3, 7)]; + + lb.addRoom(Rooms.treasure(item), BuildOrder.MIDDLE, true); + } + + + // place random foods + final List randomFood = new ArrayList<>(); + randomFood.add(Items.CHEESE); + randomFood.add(Items.MEAT); + + for (int level = 1; level <= 7; level++) { + + final LevelBuilder lb = levelBuilders[level - 1]; + final Range amount = Range.make(1, level); + + for (int i = 0; i < amount.randInt(rand); i++) { + lb.addItem(Calc.pick(rand, randomFood).createItem(), false); + } + } + + + // place monsters + for (int level = 1; level <= 7; level++) { + + final LevelBuilder lb = levelBuilders[level - 1]; + + final Range amount = Range.make(2 + level * 2, 5 + level * 3.5); + + for (int i = 0; i < amount.randInt(rand); i++) { + Entity e; + + if (level > 2 && rand.nextInt(7 - level + 1) == 0) { + e = Entities.RAT_BROWN.createEntity(); + } else { + e = Entities.RAT_GRAY.createEntity(); + } + + lb.addEntity(e, false); + } + } + + + // compile levels + for (final LevelBuilder lb : levelBuilders) { + w.addLevel(lb.build(w)); + } + + w.createPlayer(); return w; } } + + public static LevelBuilder prepareFloor(long seed, int floor, MapTheme theme, boolean lastLevel) throws WorldGenError + { + Log.f3("Generating level: " + floor); + + final LevelBuilder lb = new LevelBuilder(128, theme, seed); + + lb.addRoom(Rooms.ENTRANCE, BuildOrder.FIRST, true); + + lb.addRoom(Rooms.BASIC, Range.make(1 + floor, 1 + floor * 1.5), BuildOrder.MIDDLE, false); + lb.addRoom(Rooms.DEAD_END, Range.make(0, 1 + floor * 0.6), BuildOrder.MIDDLE, false); + lb.addRoom(Rooms.STORAGE, Range.make(1, Math.ceil(floor / 2D)), BuildOrder.MIDDLE, false); + + if (lastLevel) lb.addRoom(Rooms.BOSS, BuildOrder.LAST, true); + if (!lastLevel) lb.addRoom(Rooms.EXIT, BuildOrder.LAST, true); + + final RoomBuilder heartRoom = Rooms.shrine(Items.HEART_PIECE.createItem()); + lb.addRoom(heartRoom, BuildOrder.LAST, true); + + return lb; + } } diff --git a/src/mightypork/rogue/world/gen/rooms/AbstractRectRoom.java b/src/mightypork/rogue/world/gen/rooms/AbstractRectRoom.java index acebd14..04caaa2 100644 --- a/src/mightypork/rogue/world/gen/rooms/AbstractRectRoom.java +++ b/src/mightypork/rogue/world/gen/rooms/AbstractRectRoom.java @@ -5,10 +5,10 @@ import java.util.Random; import mightypork.gamecore.util.annot.DefaultImpl; import mightypork.gamecore.util.math.algo.Coord; -import mightypork.gamecore.util.math.algo.Sides; +import mightypork.gamecore.util.math.algo.Moves; import mightypork.rogue.world.gen.MapTheme; import mightypork.rogue.world.gen.RoomBuilder; -import mightypork.rogue.world.gen.RoomDesc; +import mightypork.rogue.world.gen.RoomEntry; import mightypork.rogue.world.gen.ScratchMap; import mightypork.rogue.world.gen.TileProtectLevel; import mightypork.rogue.world.tile.TileModel; @@ -17,7 +17,7 @@ import mightypork.rogue.world.tile.TileModel; public abstract class AbstractRectRoom implements RoomBuilder { @Override - public RoomDesc buildToFit(ScratchMap map, MapTheme theme, Random rand, Coord center) + public RoomEntry buildRoom(ScratchMap map, MapTheme theme, Random rand, Coord center) { // half width, half height actually final Coord innerSize = getInnerSize(rand); @@ -42,7 +42,7 @@ public abstract class AbstractRectRoom implements RoomBuilder { buildExtras(map, theme, rand, min, max); - return new RoomDesc(min.add(-1, -1), max); + return new RoomEntry(min.add(-1, -1), max); } @@ -84,7 +84,7 @@ public abstract class AbstractRectRoom implements RoomBuilder { break; } - if ((map.findDoors(door) & Sides.MASK_CARDINAL) == 0) { + if ((map.findDoors(door) & Moves.BITS_CARDINAL) == 0) { map.set(door, getDoorType(theme, rand)); i++; // increment pointer } diff --git a/src/mightypork/rogue/world/gen/rooms/BossRoom.java b/src/mightypork/rogue/world/gen/rooms/BossRoom.java index f948d78..97108a6 100644 --- a/src/mightypork/rogue/world/gen/rooms/BossRoom.java +++ b/src/mightypork/rogue/world/gen/rooms/BossRoom.java @@ -3,7 +3,6 @@ package mightypork.rogue.world.gen.rooms; import java.util.Random; -import mightypork.gamecore.util.math.Calc; import mightypork.gamecore.util.math.algo.Coord; import mightypork.rogue.world.entity.Entities; import mightypork.rogue.world.entity.Entity; @@ -17,21 +16,42 @@ public class BossRoom extends SecretRoom { @Override protected int getDoorCount(Random rand) { - return Calc.randInt(rand, 1, 3); + return 1; } @Override protected void buildExtras(ScratchMap map, MapTheme theme, Random rand, Coord min, Coord max) { + final Coord bossPos = min.add(3, 3); + final Entity boss = Entities.RAT_BOSS.createEntity(); - if (!map.putEntityInArea(boss, min, max, 100)) { - - // just place it anywhere then - if (!map.putEntityInMap(boss, 100)) { - throw new WorldGenError("Could not place boss."); - } - + if (!map.addEntity(boss, bossPos)) { + throw new WorldGenError("Could not place boss."); } + + Entity rat; + + // 4 guardian rats + + rat = Entities.RAT_BROWN.createEntity(); + map.addEntity(rat, min.add(1, 1)); + + rat = Entities.RAT_BROWN.createEntity(); + map.addEntity(rat, Coord.make(max.x - 1, min.y + 1)); + + rat = Entities.RAT_BROWN.createEntity(); + map.addEntity(rat, max.add(-1, -1)); + + rat = Entities.RAT_BROWN.createEntity(); + map.addEntity(rat, Coord.make(min.x + 1, max.y - 1)); + + } + + + @Override + protected Coord getInnerSize(Random rand) + { + return Coord.make(5, 5); } } diff --git a/src/mightypork/rogue/world/gen/rooms/DeadEndRoom.java b/src/mightypork/rogue/world/gen/rooms/DeadEndRoom.java index 2d8a52d..17a00fe 100644 --- a/src/mightypork/rogue/world/gen/rooms/DeadEndRoom.java +++ b/src/mightypork/rogue/world/gen/rooms/DeadEndRoom.java @@ -6,14 +6,14 @@ import java.util.Random; import mightypork.gamecore.util.math.algo.Coord; import mightypork.rogue.world.gen.MapTheme; import mightypork.rogue.world.gen.RoomBuilder; -import mightypork.rogue.world.gen.RoomDesc; +import mightypork.rogue.world.gen.RoomEntry; import mightypork.rogue.world.gen.ScratchMap; public class DeadEndRoom implements RoomBuilder { @Override - public RoomDesc buildToFit(ScratchMap map, MapTheme theme, Random rand, Coord center) + public RoomEntry buildRoom(ScratchMap map, MapTheme theme, Random rand, Coord center) { final Coord low = center.add(-1, -1); final Coord high = center; @@ -21,6 +21,6 @@ public class DeadEndRoom implements RoomBuilder { map.set(center, theme.floor()); - return new RoomDesc(low, high); + return new RoomEntry(low, high); } } diff --git a/src/mightypork/rogue/world/gen/rooms/HeartPieceRoom.java b/src/mightypork/rogue/world/gen/rooms/ItemShrineRoom.java similarity index 61% rename from src/mightypork/rogue/world/gen/rooms/HeartPieceRoom.java rename to src/mightypork/rogue/world/gen/rooms/ItemShrineRoom.java index 543ad58..51fa884 100644 --- a/src/mightypork/rogue/world/gen/rooms/HeartPieceRoom.java +++ b/src/mightypork/rogue/world/gen/rooms/ItemShrineRoom.java @@ -9,30 +9,33 @@ import mightypork.rogue.world.gen.MapTheme; import mightypork.rogue.world.gen.ScratchMap; import mightypork.rogue.world.gen.WorldGenError; import mightypork.rogue.world.item.Item; -import mightypork.rogue.world.item.Items; -public class HeartPieceRoom extends SecretRoom { +public class ItemShrineRoom extends SecretRoom { + + private final Item item; + + + public ItemShrineRoom(Item item) + { + this.item = item; + } + @Override protected int getDoorCount(Random rand) { - return Calc.randInt(rand, 1, 3); + return Calc.randInt(rand, 1, 4); } @Override protected void buildExtras(ScratchMap map, MapTheme theme, Random rand, Coord min, Coord max) { - final Item heart = Items.HEART_PIECE.createItem(); + final Coord center = min.add(2, 2); - if (!map.putItem(heart, min.add(2, 2))) { - if (!map.putItemInArea(heart, min, max, 100)) { - if (!map.putItemInMap(heart, 100)) { - throw new WorldGenError("Could not place heart piece."); - } - - } + if (!map.addItem(item, center)) { + throw new WorldGenError("Could not place item in chest."); } } diff --git a/src/mightypork/rogue/world/gen/rooms/Rooms.java b/src/mightypork/rogue/world/gen/rooms/Rooms.java deleted file mode 100644 index b718b19..0000000 --- a/src/mightypork/rogue/world/gen/rooms/Rooms.java +++ /dev/null @@ -1,16 +0,0 @@ -package mightypork.rogue.world.gen.rooms; - - -import mightypork.rogue.world.gen.RoomBuilder; - - -public class Rooms { - - public static final RoomBuilder BASIC = new BasicRoom(); - public static final RoomBuilder TREASURE = new TreasureRoom(); - public static final RoomBuilder DEAD_END = new DeadEndRoom(); - public static final RoomBuilder ENTRANCE = new EntranceRoom(); - public static final RoomBuilder EXIT = new ExitRoom(); - public static final RoomBuilder BOSS = new BossRoom(); - public static final RoomBuilder HEART_ROOM = new HeartPieceRoom(); -} diff --git a/src/mightypork/rogue/world/gen/rooms/StorageRoom.java b/src/mightypork/rogue/world/gen/rooms/StorageRoom.java new file mode 100644 index 0000000..d040e8f --- /dev/null +++ b/src/mightypork/rogue/world/gen/rooms/StorageRoom.java @@ -0,0 +1,50 @@ +package mightypork.rogue.world.gen.rooms; + + +import java.util.Random; + +import mightypork.gamecore.util.math.Calc; +import mightypork.gamecore.util.math.algo.Coord; +import mightypork.rogue.world.gen.MapTheme; +import mightypork.rogue.world.gen.ScratchMap; +import mightypork.rogue.world.item.Items; + + +public class StorageRoom extends SecretRoom { + + @Override + protected void buildExtras(ScratchMap map, MapTheme theme, Random rand, Coord min, Coord max) + { + int maxStuff = 3; + + for (int i = 0; i < Calc.randInt(rand, 0, 1); i++) { + map.addItemInArea(Items.SANDWICH.createItem(), min, max, 50); + if (--maxStuff == 0) break; + } + + for (int i = 0; i < Calc.randInt(rand, 0, 2); i++) { + map.addItemInArea(Items.TWIG.createItemDamaged(40), min, max, 50); + if (--maxStuff == 0) break; + } + + for (int i = 0; i < Calc.randInt(rand, 0, 2); i++) { + map.addItemInArea(Items.BONE.createItemDamaged(40), min, max, 50); + if (--maxStuff == 0) break; + } + + for (int i = 0; i < Calc.randInt(rand, 0, 2); i++) { + map.addItemInArea(Items.MEAT.createItem(), min, max, 50); + if (--maxStuff == 0) break; + } + + for (int i = 0; i < Calc.randInt(rand, 0, 2); i++) { + map.addItemInArea(Items.CHEESE.createItem(), min, max, 50); + if (--maxStuff == 0) break; + } + + for (int i = 0; i < Calc.randInt(rand, 0, 1); i++) { + map.addItemInArea(Items.ROCK.createItemDamaged(30), min, max, 50); + if (--maxStuff == 0) break; + } + } +} diff --git a/src/mightypork/rogue/world/gen/rooms/TreasureChestRoom.java b/src/mightypork/rogue/world/gen/rooms/TreasureChestRoom.java new file mode 100644 index 0000000..0e46f57 --- /dev/null +++ b/src/mightypork/rogue/world/gen/rooms/TreasureChestRoom.java @@ -0,0 +1,33 @@ +package mightypork.rogue.world.gen.rooms; + + +import java.util.Random; + +import mightypork.gamecore.util.math.algo.Coord; +import mightypork.rogue.world.gen.MapTheme; +import mightypork.rogue.world.gen.ScratchMap; +import mightypork.rogue.world.gen.TileProtectLevel; +import mightypork.rogue.world.item.Item; + + +public class TreasureChestRoom extends ItemShrineRoom { + + public TreasureChestRoom(Item item) + { + super(item); + } + + + @Override + protected void buildExtras(ScratchMap map, MapTheme theme, Random rand, Coord min, Coord max) + { + // set tile + final Coord center = min.add(2, 2); + + map.set(center, theme.chest()); + map.protect(center, TileProtectLevel.STRONG); + + // drop item + super.buildExtras(map, theme, rand, min, max); + } +} diff --git a/src/mightypork/rogue/world/gen/rooms/TreasureRoom.java b/src/mightypork/rogue/world/gen/rooms/TreasureRoom.java deleted file mode 100644 index 49875b0..0000000 --- a/src/mightypork/rogue/world/gen/rooms/TreasureRoom.java +++ /dev/null @@ -1,38 +0,0 @@ -package mightypork.rogue.world.gen.rooms; - - -import java.util.Random; - -import mightypork.gamecore.util.math.Calc; -import mightypork.gamecore.util.math.algo.Coord; -import mightypork.rogue.world.gen.MapTheme; -import mightypork.rogue.world.gen.ScratchMap; -import mightypork.rogue.world.item.Items; - - -public class TreasureRoom extends SecretRoom { - - @Override - protected void buildExtras(ScratchMap map, MapTheme theme, Random rand, Coord min, Coord max) - { - for (int i = 0; i < Calc.randInt(rand, 0, 1); i++) { - map.putItemInArea(Items.SANDWICH.createItem(), min, max, 50); - } - - for (int i = 0; i < Calc.randInt(rand, 0, 2); i++) { - map.putItemInArea(Items.BONE.createItemDamaged(20), min, max, 50); - } - - for (int i = 0; i < Calc.randInt(rand, 0, 1); i++) { - map.putItemInArea(Items.ROCK.createItemDamaged(30), min, max, 50); - } - - for (int i = 0; i < Calc.randInt(rand, 0, 3); i++) { - map.putItemInArea(Items.MEAT.createItem(), min, max, 50); - } - - for (int i = 0; i < Calc.randInt(rand, 0, 2); i++) { - map.putItemInArea(Items.CHEESE.createItem(), min, max, 50); - } - } -} diff --git a/src/mightypork/rogue/world/gen/themes/ThemeBrick.java b/src/mightypork/rogue/world/gen/themes/ThemeBrick.java index 830ff67..b9535ca 100644 --- a/src/mightypork/rogue/world/gen/themes/ThemeBrick.java +++ b/src/mightypork/rogue/world/gen/themes/ThemeBrick.java @@ -56,4 +56,11 @@ public class ThemeBrick implements MapTheme { { return Tiles.BRICK_EXIT; } + + + @Override + public TileModel chest() + { + return Tiles.BRICK_CHEST; + } } diff --git a/src/mightypork/rogue/world/gui/interaction/MIPKeyboard.java b/src/mightypork/rogue/world/gui/interaction/MIPKeyboard.java index 61921f6..fa7e3bb 100644 --- a/src/mightypork/rogue/world/gui/interaction/MIPKeyboard.java +++ b/src/mightypork/rogue/world/gui/interaction/MIPKeyboard.java @@ -6,8 +6,8 @@ import mightypork.gamecore.input.InputSystem; import mightypork.gamecore.input.Keys; import mightypork.gamecore.input.events.KeyEvent; import mightypork.gamecore.input.events.KeyListener; -import mightypork.gamecore.util.math.algo.Sides; -import mightypork.gamecore.util.math.algo.Step; +import mightypork.gamecore.util.math.algo.Move; +import mightypork.gamecore.util.math.algo.Moves; import mightypork.gamecore.util.math.constraints.vect.Vect; import mightypork.rogue.world.entity.impl.PlayerEntity; import mightypork.rogue.world.events.PlayerStepEndListener; @@ -17,7 +17,7 @@ import mightypork.rogue.world.gui.MapView; public class MIPKeyboard extends MapInteractionPlugin implements PlayerStepEndListener, KeyListener, Updateable { private static final int[] keys = { Keys.LEFT, Keys.RIGHT, Keys.UP, Keys.DOWN }; - private static final Step[] sides = { Sides.W, Sides.E, Sides.N, Sides.S }; + private static final Move[] sides = { Moves.W, Moves.E, Moves.N, Moves.S }; public MIPKeyboard(MapView mapView) @@ -72,7 +72,7 @@ public class MIPKeyboard extends MapInteractionPlugin implements PlayerStepEndLi for (int i = 0; i < 4; i++) { if (InputSystem.isKeyDown(keys[i])) { - final Step side = sides[i]; + final Move side = sides[i]; if (mapView.plc.canGo(side)) { mapView.plc.go(side); return true; diff --git a/src/mightypork/rogue/world/gui/interaction/MIPMouse.java b/src/mightypork/rogue/world/gui/interaction/MIPMouse.java index 72084c3..a7a704d 100644 --- a/src/mightypork/rogue/world/gui/interaction/MIPMouse.java +++ b/src/mightypork/rogue/world/gui/interaction/MIPMouse.java @@ -6,7 +6,7 @@ import mightypork.gamecore.input.InputSystem; import mightypork.gamecore.util.math.Calc.Deg; import mightypork.gamecore.util.math.Polar; import mightypork.gamecore.util.math.algo.Coord; -import mightypork.gamecore.util.math.algo.Sides; +import mightypork.gamecore.util.math.algo.Moves; import mightypork.gamecore.util.math.constraints.vect.Vect; import mightypork.rogue.world.entity.impl.PlayerEntity; import mightypork.rogue.world.events.PlayerStepEndListener; @@ -97,16 +97,16 @@ public class MIPMouse extends MapInteractionPlugin implements PlayerStepEndListe switch (dir) { case 0: - return mapView.plc.tryGo(Sides.E); + return mapView.plc.tryGo(Moves.E); case 1: - return mapView.plc.tryGo(Sides.S); + return mapView.plc.tryGo(Moves.S); case 2: - return mapView.plc.tryGo(Sides.W); + return mapView.plc.tryGo(Moves.W); case 3: - return mapView.plc.tryGo(Sides.N); + return mapView.plc.tryGo(Moves.N); } return false; diff --git a/src/mightypork/rogue/world/item/Items.java b/src/mightypork/rogue/world/item/Items.java index cade8e7..66197d8 100644 --- a/src/mightypork/rogue/world/item/Items.java +++ b/src/mightypork/rogue/world/item/Items.java @@ -10,11 +10,7 @@ import mightypork.rogue.world.item.impl.active.ItemHeartPiece; import mightypork.rogue.world.item.impl.food.ItemCheese; import mightypork.rogue.world.item.impl.food.ItemMeat; import mightypork.rogue.world.item.impl.food.ItemSandwich; -import mightypork.rogue.world.item.impl.weapons.ItemBone; -import mightypork.rogue.world.item.impl.weapons.ItemClub; -import mightypork.rogue.world.item.impl.weapons.ItemHammer; -import mightypork.rogue.world.item.impl.weapons.ItemStone; -import mightypork.rogue.world.item.impl.weapons.ItemSword; +import mightypork.rogue.world.item.impl.weapons.*; /** @@ -31,10 +27,12 @@ public final class Items { public static final ItemModel BONE = new ItemModel(3, ItemBone.class); public static final ItemModel SANDWICH = new ItemModel(4, ItemSandwich.class); public static final ItemModel CLUB = new ItemModel(5, ItemClub.class); - public static final ItemModel HAMMER = new ItemModel(6, ItemHammer.class); + public static final ItemModel AXE = new ItemModel(6, ItemAxe.class); public static final ItemModel SWORD = new ItemModel(7, ItemSword.class); - public static final ItemModel ROCK = new ItemModel(8, ItemStone.class); + public static final ItemModel ROCK = new ItemModel(8, ItemRock.class); public static final ItemModel HEART_PIECE = new ItemModel(9, ItemHeartPiece.class); + public static final ItemModel TWIG = new ItemModel(10, ItemTwig.class); + public static final ItemModel KNIFE = new ItemModel(11, ItemKnife.class); public static void register(int id, ItemModel model) diff --git a/src/mightypork/rogue/world/item/impl/active/ItemHeartPiece.java b/src/mightypork/rogue/world/item/impl/active/ItemHeartPiece.java index 243e161..364d887 100644 --- a/src/mightypork/rogue/world/item/impl/active/ItemHeartPiece.java +++ b/src/mightypork/rogue/world/item/impl/active/ItemHeartPiece.java @@ -21,7 +21,7 @@ public class ItemHeartPiece extends Item { @Override protected ItemRenderer makeRenderer() { - return new QuadItemRenderer(this, Res.txq("item.heart_piece")); + return new QuadItemRenderer(this, Res.getTxQuad("item.heart_piece")); } diff --git a/src/mightypork/rogue/world/item/impl/food/ItemCheese.java b/src/mightypork/rogue/world/item/impl/food/ItemCheese.java index b93cb92..9cadd62 100644 --- a/src/mightypork/rogue/world/item/impl/food/ItemCheese.java +++ b/src/mightypork/rogue/world/item/impl/food/ItemCheese.java @@ -19,7 +19,7 @@ public class ItemCheese extends ItemBaseFood { @Override protected ItemRenderer makeRenderer() { - return new QuadItemRenderer(this, Res.txq("item.cheese")); + return new QuadItemRenderer(this, Res.getTxQuad("item.cheese")); } diff --git a/src/mightypork/rogue/world/item/impl/food/ItemMeat.java b/src/mightypork/rogue/world/item/impl/food/ItemMeat.java index ff52066..f3359f8 100644 --- a/src/mightypork/rogue/world/item/impl/food/ItemMeat.java +++ b/src/mightypork/rogue/world/item/impl/food/ItemMeat.java @@ -19,7 +19,7 @@ public class ItemMeat extends ItemBaseFood { @Override protected ItemRenderer makeRenderer() { - return new QuadItemRenderer(this, Res.txq("item.meat")); + return new QuadItemRenderer(this, Res.getTxQuad("item.meat")); } diff --git a/src/mightypork/rogue/world/item/impl/food/ItemSandwich.java b/src/mightypork/rogue/world/item/impl/food/ItemSandwich.java index 1cf185c..940b7d8 100644 --- a/src/mightypork/rogue/world/item/impl/food/ItemSandwich.java +++ b/src/mightypork/rogue/world/item/impl/food/ItemSandwich.java @@ -19,7 +19,7 @@ public class ItemSandwich extends ItemBaseFood { @Override protected ItemRenderer makeRenderer() { - return new QuadItemRenderer(this, Res.txq("item.sandwich")); + return new QuadItemRenderer(this, Res.getTxQuad("item.sandwich")); } diff --git a/src/mightypork/rogue/world/item/impl/weapons/ItemHammer.java b/src/mightypork/rogue/world/item/impl/weapons/ItemAxe.java similarity index 75% rename from src/mightypork/rogue/world/item/impl/weapons/ItemHammer.java rename to src/mightypork/rogue/world/item/impl/weapons/ItemAxe.java index d727d7b..f337b5c 100644 --- a/src/mightypork/rogue/world/item/impl/weapons/ItemHammer.java +++ b/src/mightypork/rogue/world/item/impl/weapons/ItemAxe.java @@ -8,9 +8,9 @@ import mightypork.rogue.world.item.impl.ItemBaseWeapon; import mightypork.rogue.world.item.render.QuadItemRenderer; -public class ItemHammer extends ItemBaseWeapon { +public class ItemAxe extends ItemBaseWeapon { - public ItemHammer(ItemModel model) + public ItemAxe(ItemModel model) { super(model); } @@ -19,7 +19,7 @@ public class ItemHammer extends ItemBaseWeapon { @Override protected ItemRenderer makeRenderer() { - return new QuadItemRenderer(this, Res.txq("item.hammer")); + return new QuadItemRenderer(this, Res.getTxQuad("item.axe")); } @@ -33,13 +33,13 @@ public class ItemHammer extends ItemBaseWeapon { @Override public int getMaxUses() { - return 100; + return 70; } @Override public String getVisualName() { - return "Hammer"; + return "Axe"; } } diff --git a/src/mightypork/rogue/world/item/impl/weapons/ItemBone.java b/src/mightypork/rogue/world/item/impl/weapons/ItemBone.java index 566e3e9..ffe9631 100644 --- a/src/mightypork/rogue/world/item/impl/weapons/ItemBone.java +++ b/src/mightypork/rogue/world/item/impl/weapons/ItemBone.java @@ -19,7 +19,7 @@ public class ItemBone extends ItemBaseWeapon { @Override protected ItemRenderer makeRenderer() { - return new QuadItemRenderer(this, Res.txq("item.bone")); + return new QuadItemRenderer(this, Res.getTxQuad("item.bone")); } diff --git a/src/mightypork/rogue/world/item/impl/weapons/ItemClub.java b/src/mightypork/rogue/world/item/impl/weapons/ItemClub.java index 088867f..877cd51 100644 --- a/src/mightypork/rogue/world/item/impl/weapons/ItemClub.java +++ b/src/mightypork/rogue/world/item/impl/weapons/ItemClub.java @@ -19,7 +19,7 @@ public class ItemClub extends ItemBaseWeapon { @Override protected ItemRenderer makeRenderer() { - return new QuadItemRenderer(this, Res.txq("item.club")); + return new QuadItemRenderer(this, Res.getTxQuad("item.club")); } diff --git a/src/mightypork/rogue/world/item/impl/weapons/ItemKnife.java b/src/mightypork/rogue/world/item/impl/weapons/ItemKnife.java new file mode 100644 index 0000000..ee4fe3b --- /dev/null +++ b/src/mightypork/rogue/world/item/impl/weapons/ItemKnife.java @@ -0,0 +1,45 @@ +package mightypork.rogue.world.item.impl.weapons; + + +import mightypork.rogue.Res; +import mightypork.rogue.world.item.ItemModel; +import mightypork.rogue.world.item.ItemRenderer; +import mightypork.rogue.world.item.impl.ItemBaseWeapon; +import mightypork.rogue.world.item.render.QuadItemRenderer; + + +public class ItemKnife extends ItemBaseWeapon { + + public ItemKnife(ItemModel model) + { + super(model); + } + + + @Override + protected ItemRenderer makeRenderer() + { + return new QuadItemRenderer(this, Res.getTxQuad("item.knife")); + } + + + @Override + public int getAttackPoints() + { + return 4; + } + + + @Override + public int getMaxUses() + { + return 60; + } + + + @Override + public String getVisualName() + { + return "Knife"; + } +} diff --git a/src/mightypork/rogue/world/item/impl/weapons/ItemStone.java b/src/mightypork/rogue/world/item/impl/weapons/ItemRock.java similarity index 79% rename from src/mightypork/rogue/world/item/impl/weapons/ItemStone.java rename to src/mightypork/rogue/world/item/impl/weapons/ItemRock.java index 05ddabf..150c20b 100644 --- a/src/mightypork/rogue/world/item/impl/weapons/ItemStone.java +++ b/src/mightypork/rogue/world/item/impl/weapons/ItemRock.java @@ -8,9 +8,9 @@ import mightypork.rogue.world.item.impl.ItemBaseWeapon; import mightypork.rogue.world.item.render.QuadItemRenderer; -public class ItemStone extends ItemBaseWeapon { +public class ItemRock extends ItemBaseWeapon { - public ItemStone(ItemModel model) + public ItemRock(ItemModel model) { super(model); } @@ -19,7 +19,7 @@ public class ItemStone extends ItemBaseWeapon { @Override protected ItemRenderer makeRenderer() { - return new QuadItemRenderer(this, Res.txq("item.stone")); + return new QuadItemRenderer(this, Res.getTxQuad("item.stone")); } diff --git a/src/mightypork/rogue/world/item/impl/weapons/ItemSword.java b/src/mightypork/rogue/world/item/impl/weapons/ItemSword.java index 513dd8d..0fd8d39 100644 --- a/src/mightypork/rogue/world/item/impl/weapons/ItemSword.java +++ b/src/mightypork/rogue/world/item/impl/weapons/ItemSword.java @@ -19,14 +19,14 @@ public class ItemSword extends ItemBaseWeapon { @Override protected ItemRenderer makeRenderer() { - return new QuadItemRenderer(this, Res.txq("item.sword")); + return new QuadItemRenderer(this, Res.getTxQuad("item.sword")); } @Override public int getAttackPoints() { - return 6; + return 5; } diff --git a/src/mightypork/rogue/world/item/impl/weapons/ItemTwig.java b/src/mightypork/rogue/world/item/impl/weapons/ItemTwig.java new file mode 100644 index 0000000..d77e85e --- /dev/null +++ b/src/mightypork/rogue/world/item/impl/weapons/ItemTwig.java @@ -0,0 +1,45 @@ +package mightypork.rogue.world.item.impl.weapons; + + +import mightypork.rogue.Res; +import mightypork.rogue.world.item.ItemModel; +import mightypork.rogue.world.item.ItemRenderer; +import mightypork.rogue.world.item.impl.ItemBaseWeapon; +import mightypork.rogue.world.item.render.QuadItemRenderer; + + +public class ItemTwig extends ItemBaseWeapon { + + public ItemTwig(ItemModel model) + { + super(model); + } + + + @Override + protected ItemRenderer makeRenderer() + { + return new QuadItemRenderer(this, Res.getTxQuad("item.twig")); + } + + + @Override + public int getAttackPoints() + { + return 1; + } + + + @Override + public int getMaxUses() + { + return 10; + } + + + @Override + public String getVisualName() + { + return "Twig"; + } +} diff --git a/src/mightypork/rogue/world/level/Level.java b/src/mightypork/rogue/world/level/Level.java index e4a11a2..21e3e9e 100644 --- a/src/mightypork/rogue/world/level/Level.java +++ b/src/mightypork/rogue/world/level/Level.java @@ -16,8 +16,8 @@ import mightypork.gamecore.util.ion.IonObjBinary; import mightypork.gamecore.util.ion.IonOutput; import mightypork.gamecore.util.math.Calc; import mightypork.gamecore.util.math.algo.Coord; -import mightypork.gamecore.util.math.algo.Sides; -import mightypork.gamecore.util.math.algo.Step; +import mightypork.gamecore.util.math.algo.Move; +import mightypork.gamecore.util.math.algo.Moves; import mightypork.gamecore.util.math.algo.floodfill.FloodFill; import mightypork.gamecore.util.math.constraints.vect.Vect; import mightypork.gamecore.util.math.noise.NoiseGen; @@ -63,9 +63,9 @@ public class Level implements BusAccess, Updateable, DelegatingClient, Toggleabl private final FloodFill exploreFiller = new FloodFill() { @Override - public Step[] getSpreadSides() + public List getSpreadSides() { - return Sides.ALL_SIDES; + return Moves.ALL_SIDES; } diff --git a/src/mightypork/rogue/world/level/render/TileRenderContext.java b/src/mightypork/rogue/world/level/render/TileRenderContext.java index 1101310..0072355 100644 --- a/src/mightypork/rogue/world/level/render/TileRenderContext.java +++ b/src/mightypork/rogue/world/level/render/TileRenderContext.java @@ -2,7 +2,7 @@ package mightypork.rogue.world.level.render; import mightypork.gamecore.util.math.algo.Coord; -import mightypork.gamecore.util.math.algo.Step; +import mightypork.gamecore.util.math.algo.Move; import mightypork.gamecore.util.math.constraints.rect.Rect; import mightypork.gamecore.util.math.constraints.rect.proxy.RectBound; import mightypork.gamecore.util.math.noise.NoiseGen; @@ -46,7 +46,7 @@ public final class TileRenderContext extends MapRenderContext implements RectBou * @param offset offsets * @return the tile at that position */ - public Tile getAdjacentTile(Step offset) + public Tile getAdjacentTile(Move offset) { return map.getTile(pos.add(offset)); } diff --git a/src/mightypork/rogue/world/tile/Tile.java b/src/mightypork/rogue/world/tile/Tile.java index 03d7606..f2197f7 100644 --- a/src/mightypork/rogue/world/tile/Tile.java +++ b/src/mightypork/rogue/world/tile/Tile.java @@ -98,6 +98,8 @@ public abstract class Tile implements BusAccess, IonObjBlob { @DefaultImpl public void renderExtra(TileRenderContext context) { + initRenderer(); + renderer.renderExtra(context); } diff --git a/src/mightypork/rogue/world/tile/TileColors.java b/src/mightypork/rogue/world/tile/TileColors.java new file mode 100644 index 0000000..0ba6a27 --- /dev/null +++ b/src/mightypork/rogue/world/tile/TileColors.java @@ -0,0 +1,22 @@ +package mightypork.rogue.world.tile; + + +import mightypork.gamecore.util.math.color.Color; +import mightypork.gamecore.util.math.color.pal.RGB; + + +public class TileColors { + + public static final Color NULL = RGB.NONE; + public static final Color FLOOR = RGB.GRAY_DARK; + public static final Color WALL = RGB.GRAY_LIGHT; + public static final Color DOOR = RGB.BROWN; + public static final Color COLLAPSED_WALL = RGB.GRAY; + + public static final Color ENTRANCE = RGB.CYAN; + public static final Color EXIT = Color.fromHex(0x00EA8C); + + public static final Color SECRET_DOOR_REVEALED = RGB.PINK; + public static final Color SECRET_DOOR_HIDDEN = WALL; + +} diff --git a/src/mightypork/rogue/world/tile/TileRenderer.java b/src/mightypork/rogue/world/tile/TileRenderer.java index 8834b6c..690226a 100644 --- a/src/mightypork/rogue/world/tile/TileRenderer.java +++ b/src/mightypork/rogue/world/tile/TileRenderer.java @@ -4,7 +4,8 @@ package mightypork.rogue.world.tile; import mightypork.gamecore.eventbus.events.Updateable; import mightypork.gamecore.render.Render; import mightypork.gamecore.resources.textures.TxQuad; -import mightypork.gamecore.util.math.algo.Sides; +import mightypork.gamecore.util.annot.DefaultImpl; +import mightypork.gamecore.util.math.algo.Moves; import mightypork.gamecore.util.math.constraints.rect.Rect; import mightypork.rogue.Res; import mightypork.rogue.world.level.render.TileRenderContext; @@ -44,24 +45,24 @@ public abstract class TileRenderer implements Updateable { this.tile = tile; if (!inited) { - SH_N = Res.txq("tile.shadow.n"); - SH_S = Res.txq("tile.shadow.s"); - SH_E = Res.txq("tile.shadow.e"); - SH_W = Res.txq("tile.shadow.w"); - SH_NW = Res.txq("tile.shadow.nw"); - SH_NE = Res.txq("tile.shadow.ne"); - SH_SW = Res.txq("tile.shadow.sw"); - SH_SE = Res.txq("tile.shadow.se"); + SH_N = Res.getTxQuad("tile.shadow.n"); + SH_S = Res.getTxQuad("tile.shadow.s"); + SH_E = Res.getTxQuad("tile.shadow.e"); + SH_W = Res.getTxQuad("tile.shadow.w"); + SH_NW = Res.getTxQuad("tile.shadow.nw"); + SH_NE = Res.getTxQuad("tile.shadow.ne"); + SH_SW = Res.getTxQuad("tile.shadow.sw"); + SH_SE = Res.getTxQuad("tile.shadow.se"); - UFOG_N = Res.txq("tile.ufog.n"); - UFOG_S = Res.txq("tile.ufog.s"); - UFOG_E = Res.txq("tile.ufog.e"); - UFOG_W = Res.txq("tile.ufog.w"); - UFOG_NW = Res.txq("tile.ufog.nw"); - UFOG_NE = Res.txq("tile.ufog.ne"); - UFOG_SW = Res.txq("tile.ufog.sw"); - UFOG_SE = Res.txq("tile.ufog.se"); - UFOG_FULL = Res.txq("tile.ufog.full"); + UFOG_N = Res.getTxQuad("tile.ufog.n"); + UFOG_S = Res.getTxQuad("tile.ufog.s"); + UFOG_E = Res.getTxQuad("tile.ufog.e"); + UFOG_W = Res.getTxQuad("tile.ufog.w"); + UFOG_NW = Res.getTxQuad("tile.ufog.nw"); + UFOG_NE = Res.getTxQuad("tile.ufog.ne"); + UFOG_SW = Res.getTxQuad("tile.ufog.sw"); + UFOG_SE = Res.getTxQuad("tile.ufog.se"); + UFOG_FULL = Res.getTxQuad("tile.ufog.full"); inited = true; } } @@ -78,9 +79,9 @@ public abstract class TileRenderer implements Updateable { shadows = 0; // reset the mask for (int i = 0; i < 8; i++) { - final Tile t2 = context.getAdjacentTile(Sides.get(i)); + final Tile t2 = context.getAdjacentTile(Moves.getSide(i)); if (!t2.isNull() && t2.doesCastShadow()) { - shadows |= Sides.bit(i); + shadows |= Moves.getBit(i); } } @@ -90,16 +91,22 @@ public abstract class TileRenderer implements Updateable { if (shadows == 0) return; final Rect rect = context.getRect(); - if ((shadows & Sides.NW_CORNER) == Sides.MASK_NW) Render.quadTextured(rect, SH_NW); - if ((shadows & Sides.MASK_N) != 0) Render.quadTextured(rect, SH_N); - if ((shadows & Sides.NE_CORNER) == Sides.MASK_NE) Render.quadTextured(rect, SH_NE); + if ((shadows & Moves.BITS_NW_CORNER) == Moves.BIT_NW) Render.quadTextured(rect, SH_NW); + if ((shadows & Moves.BIT_N) != 0) Render.quadTextured(rect, SH_N); + if ((shadows & Moves.BITS_NE_CORNER) == Moves.BIT_NE) Render.quadTextured(rect, SH_NE); - if ((shadows & Sides.MASK_W) != 0) Render.quadTextured(rect, SH_W); - if ((shadows & Sides.MASK_E) != 0) Render.quadTextured(rect, SH_E); + if ((shadows & Moves.BIT_W) != 0) Render.quadTextured(rect, SH_W); + if ((shadows & Moves.BIT_E) != 0) Render.quadTextured(rect, SH_E); - if ((shadows & Sides.SW_CORNER) == Sides.MASK_SW) Render.quadTextured(rect, SH_SW); - if ((shadows & Sides.MASK_S) != 0) Render.quadTextured(rect, SH_S); - if ((shadows & Sides.SE_CORNER) == Sides.MASK_SE) Render.quadTextured(rect, SH_SE); + if ((shadows & Moves.BITS_SW_CORNER) == Moves.BIT_SW) Render.quadTextured(rect, SH_SW); + if ((shadows & Moves.BIT_S) != 0) Render.quadTextured(rect, SH_S); + if ((shadows & Moves.BITS_SE_CORNER) == Moves.BIT_SE) Render.quadTextured(rect, SH_SE); + } + + + @DefaultImpl + public void renderExtra(TileRenderContext context) + { } @@ -117,24 +124,24 @@ public abstract class TileRenderer implements Updateable { byte ufog = 0; for (int i = 0; i < 8; i++) { - final Tile t2 = context.getAdjacentTile(Sides.get(i)); + final Tile t2 = context.getAdjacentTile(Moves.getSide(i)); if (t2.isNull() || !t2.isExplored()) { - ufog |= Sides.bit(i); + ufog |= Moves.getBit(i); } } if (ufog == 0) return; - if ((ufog & Sides.NW_CORNER) == Sides.MASK_NW) Render.quadTextured(rect, UFOG_NW); - if ((ufog & Sides.MASK_N) != 0) Render.quadTextured(rect, UFOG_N); - if ((ufog & Sides.NE_CORNER) == Sides.MASK_NE) Render.quadTextured(rect, UFOG_NE); + if ((ufog & Moves.BITS_NW_CORNER) == Moves.BIT_NW) Render.quadTextured(rect, UFOG_NW); + if ((ufog & Moves.BIT_N) != 0) Render.quadTextured(rect, UFOG_N); + if ((ufog & Moves.BITS_NE_CORNER) == Moves.BIT_NE) Render.quadTextured(rect, UFOG_NE); - if ((ufog & Sides.MASK_W) != 0) Render.quadTextured(rect, UFOG_W); - if ((ufog & Sides.MASK_E) != 0) Render.quadTextured(rect, UFOG_E); + if ((ufog & Moves.BIT_W) != 0) Render.quadTextured(rect, UFOG_W); + if ((ufog & Moves.BIT_E) != 0) Render.quadTextured(rect, UFOG_E); - if ((ufog & Sides.SW_CORNER) == Sides.MASK_SW) Render.quadTextured(rect, UFOG_SW); - if ((ufog & Sides.MASK_S) != 0) Render.quadTextured(rect, UFOG_S); - if ((ufog & Sides.SE_CORNER) == Sides.MASK_SE) Render.quadTextured(rect, UFOG_SE); + if ((ufog & Moves.BITS_SW_CORNER) == Moves.BIT_SW) Render.quadTextured(rect, UFOG_SW); + if ((ufog & Moves.BIT_S) != 0) Render.quadTextured(rect, UFOG_S); + if ((ufog & Moves.BITS_SE_CORNER) == Moves.BIT_SE) Render.quadTextured(rect, UFOG_SE); } diff --git a/src/mightypork/rogue/world/tile/TileType.java b/src/mightypork/rogue/world/tile/TileType.java index 4c1a8d6..4528b51 100644 --- a/src/mightypork/rogue/world/tile/TileType.java +++ b/src/mightypork/rogue/world/tile/TileType.java @@ -2,8 +2,6 @@ package mightypork.rogue.world.tile; import mightypork.gamecore.util.math.color.Color; -import mightypork.gamecore.util.math.color.pal.PAL16; -import mightypork.gamecore.util.math.color.pal.RGB; /** @@ -14,25 +12,25 @@ import mightypork.gamecore.util.math.color.pal.RGB; public enum TileType { /** No tile */ - NULL(RGB.NONE, false), + NULL(TileColors.NULL, false), /** Floor tile */ - FLOOR(RGB.GRAY_DARK, true), + FLOOR(TileColors.FLOOR, true), /** Wall tile */ - WALL(RGB.GRAY_LIGHT, false), + WALL(TileColors.WALL, false), /** Door/gate tile */ - DOOR(PAL16.NEWPOOP, true), + DOOR(TileColors.DOOR, true), /** Passage (ie secret door) */ - PASSAGE(RGB.GRAY, true), + PASSAGE(TileColors.COLLAPSED_WALL, true), /** Stairs */ - STAIRS(RGB.CYAN, false); + STAIRS(TileColors.WALL, false); private final Color mapColor; private final boolean potentiallyWalkable; - private TileType(Color mapColor, boolean potentiallyWalkable) + private TileType(Color defaultMapColor, boolean potentiallyWalkable) { - this.mapColor = mapColor; + this.mapColor = defaultMapColor; this.potentiallyWalkable = potentiallyWalkable; } diff --git a/src/mightypork/rogue/world/tile/Tiles.java b/src/mightypork/rogue/world/tile/Tiles.java index 2e92ea8..bf3fcf5 100644 --- a/src/mightypork/rogue/world/tile/Tiles.java +++ b/src/mightypork/rogue/world/tile/Tiles.java @@ -27,6 +27,7 @@ public final class Tiles { public static final TileModel BRICK_HIDDEN_DOOR = new TileModel(14, TileBrickSecretDoor.class); public static final TileModel BRICK_ENTRANCE = new TileModel(15, TileBrickEntrance.class); public static final TileModel BRICK_EXIT = new TileModel(16, TileBrickExit.class); + public static final TileModel BRICK_CHEST = new TileModel(17, TileBrickChest.class); public static void register(int id, TileModel model) diff --git a/src/mightypork/rogue/world/tile/impl/TileBaseChest.java b/src/mightypork/rogue/world/tile/impl/TileBaseChest.java new file mode 100644 index 0000000..fb12d29 --- /dev/null +++ b/src/mightypork/rogue/world/tile/impl/TileBaseChest.java @@ -0,0 +1,101 @@ +package mightypork.rogue.world.tile.impl; + + +import java.io.IOException; + +import mightypork.gamecore.util.ion.IonInput; +import mightypork.gamecore.util.ion.IonOutput; +import mightypork.rogue.world.tile.TileModel; +import mightypork.rogue.world.tile.TileType; + + +public abstract class TileBaseChest extends TileWithItems { + + public static final double POST_OPEN_DURA = 0.3; + public boolean opened = false; + public boolean removed = false; + public double timeSinceOpen = 10000; + + private int clicks = 1; + + + public TileBaseChest(TileModel model) + { + super(model); + } + + + @Override + public TileType getType() + { + return TileType.FLOOR; + } + + + @Override + public boolean isWalkable() + { + return removed; + } + + + @Override + protected boolean shouldRenderItems() + { + return removed; + } + + + @Override + public boolean onClick() + { + if (opened & removed) return false; + + if (clicks > 0) { + clicks--; + + if (clicks == 0) { + opened = true; + timeSinceOpen = 0; + getWorld().getConsole().msgOpenChest(); + } + + } else { + if (opened && !removed && timeSinceOpen > POST_OPEN_DURA) { + removed = true; + clicks--; + } + } + + + return true; + } + + + @Override + public void save(IonOutput out) throws IOException + { + super.save(out); + out.writeIntByte(clicks); + out.writeBoolean(opened); + out.writeBoolean(removed); + } + + + @Override + public void load(IonInput in) throws IOException + { + super.load(in); + clicks = in.readIntByte(); + opened = in.readBoolean(); + removed = in.readBoolean(); + } + + + @Override + public void updateTile(double delta) + { + super.updateTile(delta); + if (opened && timeSinceOpen < 10000) timeSinceOpen += delta; + } +} diff --git a/src/mightypork/rogue/world/tile/impl/TileBaseEntrance.java b/src/mightypork/rogue/world/tile/impl/TileBaseEntrance.java index c232d5d..ed089dd 100644 --- a/src/mightypork/rogue/world/tile/impl/TileBaseEntrance.java +++ b/src/mightypork/rogue/world/tile/impl/TileBaseEntrance.java @@ -2,7 +2,9 @@ package mightypork.rogue.world.tile.impl; import mightypork.gamecore.util.math.algo.Coord; +import mightypork.gamecore.util.math.color.Color; import mightypork.rogue.world.events.WorldAscendRequest; +import mightypork.rogue.world.tile.TileColors; import mightypork.rogue.world.tile.TileModel; @@ -31,4 +33,11 @@ public abstract class TileBaseEntrance extends TileBaseStairs { { return false; } + + + @Override + public Color getMapColor() + { + return TileColors.ENTRANCE; + } } diff --git a/src/mightypork/rogue/world/tile/impl/TileBaseExit.java b/src/mightypork/rogue/world/tile/impl/TileBaseExit.java index 32432ae..47e8388 100644 --- a/src/mightypork/rogue/world/tile/impl/TileBaseExit.java +++ b/src/mightypork/rogue/world/tile/impl/TileBaseExit.java @@ -2,7 +2,9 @@ package mightypork.rogue.world.tile.impl; import mightypork.gamecore.util.math.algo.Coord; +import mightypork.gamecore.util.math.color.Color; import mightypork.rogue.world.events.WorldDescendRequest; +import mightypork.rogue.world.tile.TileColors; import mightypork.rogue.world.tile.TileModel; @@ -31,4 +33,11 @@ public abstract class TileBaseExit extends TileBaseStairs { { return false; } + + + @Override + public Color getMapColor() + { + return TileColors.EXIT; + } } diff --git a/src/mightypork/rogue/world/tile/impl/TileBaseSecretDoor.java b/src/mightypork/rogue/world/tile/impl/TileBaseSecretDoor.java index 6935fa6..c626b44 100644 --- a/src/mightypork/rogue/world/tile/impl/TileBaseSecretDoor.java +++ b/src/mightypork/rogue/world/tile/impl/TileBaseSecretDoor.java @@ -6,16 +6,14 @@ import java.io.IOException; import mightypork.gamecore.resources.textures.TxSheet; import mightypork.gamecore.util.ion.IonInput; import mightypork.gamecore.util.ion.IonOutput; -import mightypork.gamecore.util.math.Calc; import mightypork.gamecore.util.math.color.Color; -import mightypork.gamecore.util.math.color.pal.RGB; +import mightypork.rogue.world.tile.TileColors; import mightypork.rogue.world.tile.TileModel; -import mightypork.rogue.world.tile.TileType; public abstract class TileBaseSecretDoor extends TileBaseDoor { - private int clicks = Calc.randInt(2, 3); + private int clicks = 2; public TileBaseSecretDoor(TileModel model, TxSheet secret, TxSheet closed, TxSheet open) @@ -43,8 +41,8 @@ public abstract class TileBaseSecretDoor extends TileBaseDoor { @Override public Color getMapColor() { - if (locked) return TileType.WALL.getMapColor(); - return RGB.PINK; + if (locked) return TileColors.SECRET_DOOR_HIDDEN; + return TileColors.SECRET_DOOR_REVEALED; } diff --git a/src/mightypork/rogue/world/tile/impl/TileWithItems.java b/src/mightypork/rogue/world/tile/impl/TileWithItems.java index f756ac1..f8352f1 100644 --- a/src/mightypork/rogue/world/tile/impl/TileWithItems.java +++ b/src/mightypork/rogue/world/tile/impl/TileWithItems.java @@ -31,12 +31,20 @@ public abstract class TileWithItems extends Tile { @Override public void renderExtra(TileRenderContext context) { - if ((isExplored() || !Config.RENDER_UFOG) && !items.isEmpty()) { + super.renderExtra(context); + + if ((isExplored() || !Config.RENDER_UFOG) && hasItem() && shouldRenderItems()) { itemRenderer.render(items, context); } } + protected boolean shouldRenderItems() + { + return true; + } + + @Override public void updateTile(double delta) { diff --git a/src/mightypork/rogue/world/tile/impl/brick/TileBrickChest.java b/src/mightypork/rogue/world/tile/impl/brick/TileBrickChest.java new file mode 100644 index 0000000..1fd1560 --- /dev/null +++ b/src/mightypork/rogue/world/tile/impl/brick/TileBrickChest.java @@ -0,0 +1,25 @@ +package mightypork.rogue.world.tile.impl.brick; + + +import mightypork.rogue.Res; +import mightypork.rogue.world.tile.TileModel; +import mightypork.rogue.world.tile.TileRenderer; +import mightypork.rogue.world.tile.impl.TileBaseChest; +import mightypork.rogue.world.tile.render.ChestRenderer; + + +public class TileBrickChest extends TileBaseChest { + + public TileBrickChest(TileModel model) + { + super(model); + } + + + @Override + protected TileRenderer makeRenderer() + { + return new ChestRenderer(this, Res.getTxQuad("tile.brick.floor"), Res.getTxQuad("tile.extra.chest.closed"), Res.getTxQuad("tile.extra.chest.open")); + } + +} diff --git a/src/mightypork/rogue/world/tile/impl/brick/TileBrickDoor.java b/src/mightypork/rogue/world/tile/impl/brick/TileBrickDoor.java index 49a9920..535a0f2 100644 --- a/src/mightypork/rogue/world/tile/impl/brick/TileBrickDoor.java +++ b/src/mightypork/rogue/world/tile/impl/brick/TileBrickDoor.java @@ -13,9 +13,9 @@ public class TileBrickDoor extends TileBaseDoor { //@formatter:off super( model, - Res.txs("tile.brick.door.closed"), // LOCKED - Res.txs("tile.brick.door.closed"), - Res.txs("tile.brick.door.open") + Res.getTxSheet("tile.brick.door.closed"), // LOCKED + Res.getTxSheet("tile.brick.door.closed"), + Res.getTxSheet("tile.brick.door.open") ); //@formatter:on } diff --git a/src/mightypork/rogue/world/tile/impl/brick/TileBrickEntrance.java b/src/mightypork/rogue/world/tile/impl/brick/TileBrickEntrance.java index fbe673d..8181ad4 100644 --- a/src/mightypork/rogue/world/tile/impl/brick/TileBrickEntrance.java +++ b/src/mightypork/rogue/world/tile/impl/brick/TileBrickEntrance.java @@ -19,7 +19,7 @@ public class TileBrickEntrance extends TileBaseEntrance { @Override protected TileRenderer makeRenderer() { - return new OneFrameTileRenderer(this, Res.txq("tile.brick.stairs.up")); + return new OneFrameTileRenderer(this, Res.getTxQuad("tile.brick.stairs.up")); } } diff --git a/src/mightypork/rogue/world/tile/impl/brick/TileBrickExit.java b/src/mightypork/rogue/world/tile/impl/brick/TileBrickExit.java index a4c4bdd..b55843e 100644 --- a/src/mightypork/rogue/world/tile/impl/brick/TileBrickExit.java +++ b/src/mightypork/rogue/world/tile/impl/brick/TileBrickExit.java @@ -19,7 +19,7 @@ public class TileBrickExit extends TileBaseExit { @Override protected TileRenderer makeRenderer() { - return new OneFrameTileRenderer(this, Res.txq("tile.brick.stairs.down")); + return new OneFrameTileRenderer(this, Res.getTxQuad("tile.brick.stairs.down")); } } diff --git a/src/mightypork/rogue/world/tile/impl/brick/TileBrickFloor.java b/src/mightypork/rogue/world/tile/impl/brick/TileBrickFloor.java index a4768fd..d223c9c 100644 --- a/src/mightypork/rogue/world/tile/impl/brick/TileBrickFloor.java +++ b/src/mightypork/rogue/world/tile/impl/brick/TileBrickFloor.java @@ -10,7 +10,7 @@ public class TileBrickFloor extends TileBaseFloor { public TileBrickFloor(TileModel model) { - super(model, Res.txs("tile.brick.floor")); + super(model, Res.getTxSheet("tile.brick.floor")); } } diff --git a/src/mightypork/rogue/world/tile/impl/brick/TileBrickPassage.java b/src/mightypork/rogue/world/tile/impl/brick/TileBrickPassage.java index 53e6b55..c2beb61 100644 --- a/src/mightypork/rogue/world/tile/impl/brick/TileBrickPassage.java +++ b/src/mightypork/rogue/world/tile/impl/brick/TileBrickPassage.java @@ -10,7 +10,7 @@ public class TileBrickPassage extends TileBasePassage { public TileBrickPassage(TileModel model) { - super(model, Res.txs("tile.brick.passage")); + super(model, Res.getTxSheet("tile.brick.passage")); } } diff --git a/src/mightypork/rogue/world/tile/impl/brick/TileBrickSecretDoor.java b/src/mightypork/rogue/world/tile/impl/brick/TileBrickSecretDoor.java index ee0ed66..24cf67d 100644 --- a/src/mightypork/rogue/world/tile/impl/brick/TileBrickSecretDoor.java +++ b/src/mightypork/rogue/world/tile/impl/brick/TileBrickSecretDoor.java @@ -13,9 +13,9 @@ public class TileBrickSecretDoor extends TileBaseSecretDoor { //@formatter:off super( model, - Res.txs("tile.brick.door.secret"), - Res.txs("tile.brick.door.closed"), - Res.txs("tile.brick.door.open") + Res.getTxSheet("tile.brick.door.secret"), + Res.getTxSheet("tile.brick.door.closed"), + Res.getTxSheet("tile.brick.door.open") ); //@formatter:on diff --git a/src/mightypork/rogue/world/tile/impl/brick/TileBrickWall.java b/src/mightypork/rogue/world/tile/impl/brick/TileBrickWall.java index 16ee744..fea5704 100644 --- a/src/mightypork/rogue/world/tile/impl/brick/TileBrickWall.java +++ b/src/mightypork/rogue/world/tile/impl/brick/TileBrickWall.java @@ -10,7 +10,7 @@ public class TileBrickWall extends TileBaseWall { public TileBrickWall(TileModel model) { - super(model, Res.txs("tile.brick.wall")); + super(model, Res.getTxSheet("tile.brick.wall")); } } diff --git a/src/mightypork/rogue/world/tile/render/ChestRenderer.java b/src/mightypork/rogue/world/tile/render/ChestRenderer.java new file mode 100644 index 0000000..0db3ad8 --- /dev/null +++ b/src/mightypork/rogue/world/tile/render/ChestRenderer.java @@ -0,0 +1,50 @@ +package mightypork.rogue.world.tile.render; + + +import mightypork.gamecore.render.Render; +import mightypork.gamecore.resources.textures.TxQuad; +import mightypork.rogue.world.level.render.TileRenderContext; +import mightypork.rogue.world.tile.TileRenderer; +import mightypork.rogue.world.tile.impl.TileBaseChest; + + +public class ChestRenderer extends TileRenderer { + + private final TxQuad txqFloor; + private final TxQuad txqChest; + + private final TileBaseChest chestTile; + private final TxQuad txqChestOpen; + + + public ChestRenderer(TileBaseChest tile, TxQuad txq, TxQuad chest, TxQuad chestOpen) + { + super(tile); + + this.chestTile = tile; + + this.txqFloor = txq; + this.txqChest = chest; + this.txqChestOpen = chestOpen; + } + + + @Override + public void renderTile(TileRenderContext context) + { + Render.quadTextured(context.getRect(), txqFloor); + } + + + @Override + public void renderExtra(TileRenderContext context) + { + if (!chestTile.opened) { + Render.quadTextured(context.getRect(), txqChest); + } else { + if (!chestTile.removed) { + Render.quadTextured(context.getRect(), txqChestOpen); + } + } + } +}