summary refs log tree commit diff stats
Commit message (Expand)AuthorAgeFilesLines
...
* Added sfx to electrocution eventKelly Rauchenberger2021-02-135-1/+11
* Added lightning sprite to mailbox eventKelly Rauchenberger2021-02-1323-36/+190
* Added non-looping animationsKelly Rauchenberger2021-02-138-13/+67
* Player is a bit lower on screen nowKelly Rauchenberger2021-02-131-1/+1
* Added sprite shadowsKelly Rauchenberger2021-02-137-0/+16
* Got correct sprites for message arrowsKelly Rauchenberger2021-02-124-13/+18
* Added sfx for selecting message choicesKelly Rauchenberger2021-02-123-0/+8
* Added tile-sized invisible spritesKelly Rauchenberger2021-02-124-3/+46
* Scripts are organised per-map nowKelly Rauchenberger2021-02-1215-35/+44
* Added message bulletsKelly Rauchenberger2021-02-126-18/+37
* Flint uses talking animation in script0001 nowKelly Rauchenberger2021-02-112-1/+12
* Added choice promptsKelly Rauchenberger2021-02-118-39/+127
* Dark green map2 tile should be solidKelly Rauchenberger2021-02-101-2/+2
* Player movement/input is halted during cutscenesKelly Rauchenberger2021-02-107-2/+30
* Player is no longer controllable during map transitionsKelly Rauchenberger2021-02-105-6/+12
* Removed out-of-bounds tile collisionKelly Rauchenberger2021-02-101-4/+10
* loadMap requires a direction now, so party trails are set up correctlyKelly Rauchenberger2021-02-106-9/+15
* Running is preserved across screen transitionsKelly Rauchenberger2021-02-104-13/+45
* Direction is preserved across map transitions nowKelly Rauchenberger2021-02-102-1/+53
* Fixed running sound looping infinitely after running into map changeKelly Rauchenberger2021-02-103-4/+14
* Fixed cmake warnings from SDL2_image/mixerKelly Rauchenberger2021-02-103-2/+2
* Fixed audio popping from speaking tonesKelly Rauchenberger2021-02-105-0/+0
* Added fade out around map changeKelly Rauchenberger2021-02-109-12/+59
* When changing map, place whole party at the warpKelly Rauchenberger2021-02-101-4/+6
* Fixed issue with walking vertically into solid spritesKelly Rauchenberger2021-02-101-6/+6
* Map changing!Kelly Rauchenberger2021-02-0920-67/+846
* Added trigger zones to the mapKelly Rauchenberger2021-02-0910-19/+88
* Maps can contain named points of interest called warpsKelly Rauchenberger2021-02-094-2/+15
* Fixed issue with single-line messagesKelly Rauchenberger2021-02-091-0/+4
* Entity prototypes can be stored in the map file nowKelly Rauchenberger2021-02-094-6/+63
* Added newlines that don't require A pressesKelly Rauchenberger2021-02-093-9/+27
* Added "no problem here"Kelly Rauchenberger2021-02-072-0/+18
* Characters added to a party now tween to their starting position instead of t...Kelly Rauchenberger2021-02-061-2/+13
* Added function to get Direction from one point to anotherKelly Rauchenberger2021-02-061-0/+26
* Added sound and animation changes to scriptingKelly Rauchenberger2021-02-069-9/+77
* Fixed text wrapping miscalculation issueKelly Rauchenberger2021-02-061-7/+1
* Added sprite interactionKelly Rauchenberger2021-02-065-15/+50
* Added collision with other spritesKelly Rauchenberger2021-02-063-29/+105
* Added FlintKelly Rauchenberger2021-02-064-0/+334
* Added collidable object cacheKelly Rauchenberger2021-02-064-18/+96
* Moved some collision stuff into the TransformSystemKelly Rauchenberger2021-02-063-49/+88
* Prevented multiple loading of the same image filesKelly Rauchenberger2021-02-052-13/+18
* Organised resources folderKelly Rauchenberger2021-02-0536-31/+31
* Created script systemKelly Rauchenberger2021-02-0511-2/+26867
* Added pageflip message soundKelly Rauchenberger2021-02-052-0/+2
* Added little bobbing "next message" arrowKelly Rauchenberger2021-02-056-4/+51
* Added speaker headerKelly Rauchenberger2021-02-056-11/+49
* Added text boxesKelly Rauchenberger2021-02-0418-4/+474
* Added cutscene bars (and resized game)Kelly Rauchenberger2021-02-038-10/+128
* Added running soundsKelly Rauchenberger2021-02-038-5/+132
">(config["consumer_key"].as<std::string>()); auth.setConsumerSecret(config["consumer_secret"].as<std::string>()); auth.setAccessKey(config["access_key"].as<std::string>()); auth.setAccessSecret(config["access_secret"].as<std::string>()); client_ = std::unique_ptr<twitter::client>(new twitter::client(auth)); // Set up the verbly database. database_ = std::unique_ptr<verbly::database>( new verbly::database(config["verbly_datafile"].as<std::string>())); } void capital::run() const { for (;;) { std::cout << "Generating tweet..." << std::endl; try { // Find a noun to use as the pictured item. std::cout << "Choosing pictured noun..." << std::endl; verbly::word pictured = getPicturedNoun(); std::cout << "Noun: " << pictured.getBaseForm().getText() << std::endl; // Choose a picture of that noun. std::cout << "Finding an image..." << std::endl; Magick::Image image = getImageForNoun(pictured); // Generate the tweet text. std::cout << "Generating text..." << std::endl; std::string text = generateTweetText(pictured); std::cout << "Tweet text: " << text << std::endl; // Send the tweet. std::cout << "Sending tweet..." << std::endl; sendTweet(std::move(text), std::move(image)); std::cout << "Tweeted!" << std::endl; // Wait. std::this_thread::sleep_for(std::chrono::hours(1)); } catch (const could_not_get_image& ex) { std::cout << ex.what() << std::endl; } catch (const Magick::ErrorImage& ex) { std::cout << "Image error: " << ex.what() << std::endl; } catch (const Magick::ErrorCorruptImage& ex) { std::cout << "Corrupt image: " << ex.what() << std::endl; } catch (const twitter::twitter_error& ex) { std::cout << "Twitter error: " << ex.what() << std::endl; std::this_thread::sleep_for(std::chrono::hours(1)); } std::cout << std::endl; } } verbly::word capital::getPicturedNoun() const { verbly::filter whitelist = (verbly::notion::wnid == 100021939); // Artifacts verbly::filter blacklist = (verbly::notion::wnid == 106883725) // swastika || (verbly::notion::wnid == 104416901) // tetraskele || (verbly::notion::wnid == 103575691) // instrument of execution || (verbly::notion::wnid == 103829563) // noose ; verbly::query<verbly::word> pictureQuery = database_->words( (verbly::notion::fullHypernyms %= whitelist) && !(verbly::notion::fullHypernyms %= blacklist) && (verbly::notion::partOfSpeech == verbly::part_of_speech::noun) && (verbly::notion::numOfImages >= 1) // Blacklist ethnic slurs && !(verbly::word::usageDomains %= (verbly::notion::wnid == 106718862)) ); verbly::word pictured = pictureQuery.first(); return pictured; } Magick::Image capital::getImageForNoun(verbly::word pictured) const { // Accept string from Google Chrome std::string accept = "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"; curl::curl_header headers; headers.add(accept); int backoff = 0; std::cout << "Getting URLs..." << std::endl; std::string lstdata; while (lstdata.empty()) { std::ostringstream lstbuf; curl::curl_ios<std::ostringstream> lstios(lstbuf); curl::curl_easy lsthandle(lstios); std::string lsturl = pictured.getNotion().getImageNetUrl(); lsthandle.add<CURLOPT_URL>(lsturl.c_str()); try { lsthandle.perform(); } catch (const curl::curl_easy_exception& e) { e.print_traceback(); backoff++; std::cout << "Waiting for " << backoff << " seconds..." << std::endl; std::this_thread::sleep_for(std::chrono::seconds(backoff)); continue; } backoff = 0; if (lsthandle.get_info<CURLINFO_RESPONSE_CODE>().get() != 200) { throw could_not_get_image(); } std::cout << "Got URLs." << std::endl; lstdata = lstbuf.str(); } std::vector<std::string> lstvec = verbly::split<std::vector<std::string>>(lstdata, "\r\n"); if (lstvec.empty()) { throw could_not_get_image(); } std::shuffle(std::begin(lstvec), std::end(lstvec), rng_); std::deque<std::string> urls; for (std::string& url : lstvec) { urls.push_back(url); } bool found = false; Magick::Blob img; Magick::Image pic; while (!found && !urls.empty()) { std::string url = urls.front(); urls.pop_front(); std::ostringstream imgbuf; curl::curl_ios<std::ostringstream> imgios(imgbuf); curl::curl_easy imghandle(imgios); imghandle.add<CURLOPT_HTTPHEADER>(headers.get()); imghandle.add<CURLOPT_URL>(url.c_str()); imghandle.add<CURLOPT_CONNECTTIMEOUT>(30); try { imghandle.perform(); } catch (const curl::curl_easy_exception& error) { error.print_traceback(); continue; } if (imghandle.get_info<CURLINFO_RESPONSE_CODE>().get() != 200) { continue; } std::string content_type = imghandle.get_info<CURLINFO_CONTENT_TYPE>().get(); if (content_type.substr(0, 6) != "image/") { continue; } std::string imgstr = imgbuf.str(); img = Magick::Blob(imgstr.c_str(), imgstr.length()); try { pic.read(img); if (pic.rows() > 0) { std::cout << url << std::endl; found = true; } } catch (const Magick::ErrorOption& e) { // Occurs when the the data downloaded from the server is malformed std::cout << "Magick: " << e.what() << std::endl; } } if (!found) { throw could_not_get_image(); } return pic; } std::string capital::generateTweetText(verbly::word pictured) const { int msd = std::uniform_int_distribution<int>(1, 9)(rng_); int mag = std::uniform_int_distribution<int>(2, 9)(rng_); std::string money; for (int i=0; i<mag; i++) { money.insert(0, "0"); if ((i % 3 == 2) && (mag > 3)) { money.insert(0, ","); } } money.insert(0, std::to_string(msd)); money.insert(0, "$"); verbly::token nounTok = verbly::token::capitalize( verbly::token::casing::title_case, pictured); int aci = std::uniform_int_distribution<int>(0, 7)(rng_); verbly::token action; switch (aci) { case 0: action = { "No One Will Buy This", money, nounTok }; break; case 1: action = { "This", nounTok, "Is Not Worth", money }; break; case 2: action = { "We Can't Get Rid Of This", money, nounTok }; break; case 3: action = { "Millenials Will No Longer Spend", money, "For", verbly::token::indefiniteArticle(nounTok) }; break; case 4: action = { "Why Does This", money, nounTok, "Exist?" }; break; case 5: action = { "Someone Spent", money, "Making This", nounTok, "That No One Will Buy" }; break; case 6: action = { "What A Waste: This", nounTok, "Will Rot Because", "No One Can Afford Its", money, "Price Tag" }; break; case 7: action = { "This", money, nounTok, "Was A Mistake" }; break; } return action.compile(); } void capital::sendTweet(std::string text, Magick::Image image) const { Magick::Blob outputBlob; image.magick("jpg"); image.write(&outputBlob); long media_id = client_->uploadMedia("image/jpeg", (const char*) outputBlob.data(), outputBlob.length()); client_->updateStatus(std::move(text), {media_id}); }