/** * Copyright (c) 2005-2017, KoLmafia development team * http://kolmafia.sourceforge.net/ * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * [1] Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * [2] Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * [3] Neither the name "KoLmafia" nor the names of its contributors may * be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package net.sourceforge.kolmafia.request; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import net.sourceforge.kolmafia.AdventureResult; import net.sourceforge.kolmafia.AdventureResult.AdventureMultiResult; import net.sourceforge.kolmafia.KoLCharacter; import net.sourceforge.kolmafia.KoLConstants; import net.sourceforge.kolmafia.KoLConstants.MafiaState; import net.sourceforge.kolmafia.KoLmafia; import net.sourceforge.kolmafia.RequestLogger; import net.sourceforge.kolmafia.persistence.ItemDatabase; import net.sourceforge.kolmafia.preferences.Preferences; import net.sourceforge.kolmafia.session.PvpManager; import net.sourceforge.kolmafia.session.ResultProcessor; import net.sourceforge.kolmafia.utilities.StringUtilities; public class PeeVPeeRequest extends GenericRequest { public static final String[] WIN_MESSAGES = new String[] { "50 CHARACTER LIMIT BREAK!", "HERE'S YOUR CHEETO, MOTHER!*$#ER.", "If you want it back, I'll be in my tent.", "PWNED LIKE CRAPSTORM." }; public static final String[] LOSE_MESSAGES = new String[] { "OMG HAX H4X H5X!!", "Please return my pants.", "How do you like my Crotch-To-Your-Foot style?", "PWNED LIKE CRAPSTORM." }; private static final Pattern ATTACKS_PATTERN = Pattern.compile( "You have (\\d+) fight" ); private static final Pattern CHALLENGE_PATTERN1 = Pattern.compile( "<div class=\"fight\"><a.*?who=(\\d+)\"><b>(.*?)</b></a> calls out <a.*?who=(\\d+)\"><b>(.*?)</b></a> for battle!" ); private static final Pattern CHALLENGE_PATTERN2 = Pattern.compile( "<a.*?who=(\\d+)\">(.*?)</a> vs <a.*?who=(\\d+)\">(.*?)</a>" ); private static final Pattern WIN_PATTERN1 = Pattern.compile( "<span[^>]*><b>(.*?)</b> won the fight, <b>(\\d+)</b> to <b>(\\d+)</b>!" ); private static final Pattern WIN_PATTERN2 = Pattern.compile( "align=\"center\"><b>(.*?)</b> Wins!</td>" ); private static final Pattern SWAGGER_PATTERN = Pattern.compile( "You gain a little swagger <b>\\([+](\\d)\\)</b>" ); public static final Pattern RANKED_PATTERN = Pattern.compile( "ranked=([^&]*)" ); public static final Pattern WHO_PATTERN = Pattern.compile( "who=([^&]*)" ); public static final Pattern STANCE_PATTERN = Pattern.compile( "stance=([^&]*)" ); public static final Pattern MISSION_PATTERN = Pattern.compile( "attacktype=([^&]*)" ); public PeeVPeeRequest() { super( "peevpee.php" ); } public PeeVPeeRequest( final String place ) { super( "peevpee.php" ); this.addFormField( "place", place ); } public PeeVPeeRequest( final String opponent, final int stance, final String mission ) { super( "peevpee.php" ); this.addFormField( "action", "fight" ); this.addFormField( "place", "fight" ); this.addFormField( "attacktype", mission ); // ranked=1 for normal, 2 for harder this.addFormField( "ranked", "1" ); this.addFormField( "stance", String.valueOf( stance ) ); this.addFormField( "who", opponent ); String win = Preferences.getString( "defaultFlowerWinMessage" ); String lose = Preferences.getString( "defaultFlowerLossMessage" ); if ( win.equals( "" ) ) { win = PeeVPeeRequest.WIN_MESSAGES[ KoLConstants.RNG.nextInt( PeeVPeeRequest.WIN_MESSAGES.length ) ]; } if ( lose.equals( "" ) ) { lose = PeeVPeeRequest.LOSE_MESSAGES[ KoLConstants.RNG.nextInt( PeeVPeeRequest.LOSE_MESSAGES.length ) ]; } this.addFormField( "winmessage", win ); this.addFormField( "losemessage", lose ); } public void setTarget( final String target ) { this.addFormField( "who", target ); } public void setTargetType( final String type ) { this.addFormField( "ranked", type ); } public static void parseResponse( final String location, final String responseText ) { if ( location.contains( "place=shop" ) || location.contains( "action=buy" ) ) { SwaggerShopRequest.parseResponse( location, responseText ); return; } if ( location.contains( "place=fight" ) ) { Matcher attacksMatcher = PeeVPeeRequest.ATTACKS_PATTERN.matcher( responseText ); if ( attacksMatcher.find() ) { KoLCharacter.setAttacksLeft( StringUtilities.parseInt( attacksMatcher.group( 1 ) ) ); KoLCharacter.setHippyStoneBroken( true ); } else if ( responseText.contains( "You're out of fights!" ) ) { KoLCharacter.setAttacksLeft( 0 ); KoLCharacter.setHippyStoneBroken( true ); } else if ( responseText.contains( "Magical Mystical Hippy Stone" ) ) { KoLCharacter.setHippyStoneBroken( false ); } if ( location.contains( "action=fight" ) ) { // You may not attack players who are in Hardcore mode unless you are in Hardcore mode yourself. // You can't attack a player against whom you've already won a fight today. // You can't attack somebody in the same clan as you. // Sorry, I couldn't find the player "sdfsdfs". // You know, once you start hurting yourself, you won't be able to stop, // and you'll end up working for James Spader as a secretary, and // constantly letting him spank you. Let's not start down that road. // // Include <tr><td> to avoid matching player-supplied messages // // On the other hand, the following is a valid message from a loss: // You gain 0 Strengthliness. if ( responseText.contains( "<tr><td>You may not" ) || responseText.contains( "<tr><td>You can't" ) || responseText.contains( "<tr><td>You know" ) || responseText.contains( "<tr><td>Sorry" ) ) { RequestLogger.printLine( "Invalid target" ); return; } // <tr><td><p>Before entering combat, you must pledge your allegiance to a clan for the season. if ( responseText.contains( "<td><p>Before entering combat" ) ) { KoLmafia.updateDisplay( MafiaState.ABORT, "You need to pledge allegiance to a clan first." ); return; } Matcher swaggerMatcher = PeeVPeeRequest.SWAGGER_PATTERN.matcher( responseText ); if ( swaggerMatcher.find() ) { Preferences.increment( "availableSwagger", Integer.parseInt( swaggerMatcher.group(1) ) ); } boolean compactResults = false; Matcher challengeMatcher = PeeVPeeRequest.CHALLENGE_PATTERN1.matcher( responseText ); Matcher winMatcher; boolean won = false; //int id1 = 0; String me = null; //int id2 = 0; String you = null; int result1 = 0; int result2 = 0; if ( challengeMatcher.find() ) { //id1 = Integer.parseInt( challengeMatcher.group( 1 ) ); me = challengeMatcher.group( 2 ); //id2 = Integer.parseInt( challengeMatcher.group( 3 ) ); you = challengeMatcher.group( 4 ); } else { compactResults = true; challengeMatcher = PeeVPeeRequest.CHALLENGE_PATTERN2.matcher( responseText ); if ( challengeMatcher.find() ) { //id1 = Integer.parseInt( challengeMatcher.group( 1 ) ); me = challengeMatcher.group( 2 ); //id2 = Integer.parseInt( challengeMatcher.group( 3 ) ); you = challengeMatcher.group( 4 ); } } if ( !compactResults ) { winMatcher = PeeVPeeRequest.WIN_PATTERN1.matcher( responseText ); if ( winMatcher.find() ) { String winner = winMatcher.group( 1 ); won = winner.equals( me ); result1 = Integer.parseInt( winMatcher.group( 2 ) ); result2 = Integer.parseInt( winMatcher.group( 3 ) ); } } else { winMatcher = PeeVPeeRequest.WIN_PATTERN2.matcher( responseText ); if ( winMatcher.find() ) { String winner = winMatcher.group( 1 ); won = winner.equals( me ); } } if ( you == null ) { // Something went wrong. Ideally we won't get here, but this will at least // prevent looping through failed attacks KoLmafia.updateDisplay( MafiaState.ABORT, "Something went wrong with executing your PvP fights" ); return; } StringBuilder buf = new StringBuilder( "You challenged " ); buf.append( you ); buf.append( " and " ); buf.append( won ? "won" : "lost" ); buf.append( " the PvP fight" ); if ( !compactResults ) { buf.append( ", " ); buf.append( String.valueOf( won ? result1 : result2 ) ); buf.append( " to " ); buf.append( String.valueOf( won ? result2 : result1 ) ); buf.append( "!" ); } String message = buf.toString(); RequestLogger.printLine( message ); RequestLogger.updateSessionLog( message); if ( won ) { Preferences.setString( "currentPvpVictories", Preferences.getString( "currentPvpVictories" ) + you + "," ); } else if ( !compactResults ) { PeeVPeeRequest.parseStatLoss( responseText ); } } else if ( !PvpManager.stancesKnown ) { PvpManager.parseStances( responseText ); } return; } if ( location.contains( "action=smashstone" ) ) { if ( responseText.contains( "You shatter" ) ) { KoLCharacter.setAttacksLeft( 10 ); KoLCharacter.setHippyStoneBroken( true ); } } } private static final String STAT_STRING = KoLCharacter.getUserName().toLowerCase() + " lost "; private static final void parseStatLoss( final String responseText ) { String[] blocks = responseText.split( "<td>" ); for ( int i = 0; i < blocks.length; ++i ) { if ( blocks[i].toLowerCase().indexOf( STAT_STRING ) != 0 ) { continue; } String printedStatMessage = blocks[i].substring( 0, blocks[i].indexOf( ".</td>" ) ); int index = printedStatMessage.lastIndexOf( " lost " ); String statMessage = printedStatMessage.substring( index + 6 ); String[] stats = statMessage.split( " " ); int statsLost = -1 * Integer.parseInt( stats[0] ); String statname = stats[1]; int[] gained = { AdventureResult.MUS_SUBSTAT.contains( statname ) ? statsLost : 0, AdventureResult.MYS_SUBSTAT.contains( statname ) ? statsLost : 0, AdventureResult.MOX_SUBSTAT.contains( statname ) ? statsLost : 0 }; AdventureResult result = new AdventureMultiResult( AdventureResult.SUBSTATS, gained ); ResultProcessor.processResult( result ); RequestLogger.printLine( printedStatMessage ); } } public static final void parseItems( final String responseText ) { // This doesn't work in compact mode Matcher itemMatcher = ResultProcessor.ITEM_TABLE_PATTERN.matcher( responseText ); if ( itemMatcher.find() ) { String relString = itemMatcher.group( 1 ); AdventureResult item = ItemDatabase.itemFromRelString( relString ); ResultProcessor.processItem( false, "You acquire an item:", item, (List<AdventureResult>) null ); } } private static final String getField( final Pattern pattern, final String urlString ) { Matcher matcher = pattern.matcher( urlString ); return matcher.find() ? matcher.group(1) : null; } private static final String getOpponent( final String who, final String ranked ) { if ( who != null && !who.equals( "" ) ) { return who; } if ( ranked != null && ranked.equals( "1" ) ) { return "a random opponent"; } if ( ranked != null && ranked.equals( "2" ) ) { return "a random stronger opponent"; } return "an unknown opponent"; } private static final String getMission( final String mission ) { return ( mission == null ) ? "an unknown mission" : mission.equals( "lootwhatever" ) ? "loot" : mission; } private static final String getStance( final String stanceString ) { String stanceName = stanceString != null ? PvpManager.findStance( StringUtilities.parseInt( stanceString ) ) : null; return stanceName != null ? stanceName : "an unknown stance"; } public static final boolean registerRequest( final String urlString ) { if ( !urlString.startsWith( "peevpee.php" ) ) { return false; } String place = PeeVPeeRequest.getField( GenericRequest.PLACE_PATTERN, urlString ); String action = PeeVPeeRequest.getField( GenericRequest.ACTION_PATTERN, urlString ); // Don't log visits to the container document if ( place == null && action == null ) { return true; } if ( place == null ) { return false; } if ( place.equals( "rules" ) || place.equals( "boards" ) || place.equals( "logs" ) ) { return true; } if ( place.equals( "shop" ) ) { return SwaggerShopRequest.registerRequest( urlString ); } if ( action == null ) { return true; } if ( place.equals( "fight" ) ) { if ( action.equals( "fight" ) ) { String ranked = PeeVPeeRequest.getField( PeeVPeeRequest.RANKED_PATTERN, urlString ); String who = PeeVPeeRequest.getField( PeeVPeeRequest.WHO_PATTERN, urlString ); String stance = PeeVPeeRequest.getField( PeeVPeeRequest.STANCE_PATTERN, urlString ); String mission = PeeVPeeRequest.getField( PeeVPeeRequest.MISSION_PATTERN, urlString ); StringBuilder buf = new StringBuilder(); buf.append( "Attack " ); buf.append( PeeVPeeRequest.getOpponent( who, ranked ) ); buf.append( " for " ); buf.append( PeeVPeeRequest.getMission( mission ) ); buf.append( " via " ); buf.append( PeeVPeeRequest.getStance( stance ) ); String message = buf.toString(); RequestLogger.updateSessionLog(); RequestLogger.updateSessionLog( message ); } return true; } // Log anything else, for now return false; } }