collapse collapse
* User Info
 
 
Welcome, Guest. Please login or register.
* Search

* Board Stats
  • stats Total Members: 989
  • stats Total Posts: 18363
  • stats Total Topics: 2500
  • stats Total Categories: 7
  • stats Total Boards: 35
  • stats Most Online: 1144

Author Topic: Text Adventure Module  (Read 6009 times)

0 Members and 1 Guest are viewing this topic.

Offline DJKRose

  • Contributor
  • *******
  • Posts: 38
  • Karma: +0/-0
Text Adventure Module
« on: November 18, 2009, 07:34:04 pm »
Hi folks!

I've made a flexible Text Adventure Module for BeBot: Create interactive text adventures and let your guild members in-game go from chapter to chapter by clicking on options that you have defined. You can make guild advertisement with it as well!

- Simple text files to define your text adventure
- Use name tags, includes, redirects
- Enabling creative guild advertisement
- For fun or serious stuff (crafting, instance information, ...)

Download: http://tubaka.tu.funpic.de/TextAdventure.zip

1) Extract it in your bot's root directory keeping the directory structure.
2) Test the bot with the command "!textadventure example"
3) See example.txt with lots of comments for creating your own adventures.
4) Put your own adventures in directory "textadventures" with .txt extension.
5) Have a look at the seetings (you don't need to change anything): "!settings TextAdventure"

Zip file also includes a big German text adventure called "tortage" which I use for guild advertisement sometimes.

Please post your comments and own text adventures in this thread (german welcome)!

Have fun,
djkrose

Offline DJKRose

  • Contributor
  • *******
  • Posts: 38
  • Karma: +0/-0
Re: Text Adventure Module
« Reply #1 on: November 18, 2009, 07:37:27 pm »
p.s.:
There is an undocumented feature: Prepend a chapter line with "-->" to allow lateral entrance to this chapter. Useful if you want to include the first choice command in global guild spam or other scripts. Example:

--> 5. You are entering the room.
- Go right --> 6
- Go left --> 7


Directly jump into chapter 5 and trigger choice 7 with this command: !textadventure filename 5 7

Offline Drizzurdeen

  • BeBot Apprentice
  • ***
  • Posts: 193
  • Karma: +0/-0
    • Obsidian-Cult
Re: Text Adventure Module
« Reply #2 on: November 18, 2009, 11:05:07 pm »
ohhhh a very nice module ... works perfekt ;) our rp-fanatics love it ... they are know writing storries ... maybe we would post them here ... (german ;) )

greez Drizz

Offline Gauche

  • BeBot Rookie
  • *
  • Posts: 16
  • Karma: +0/-0
Re: Text Adventure Module
« Reply #3 on: November 26, 2009, 05:04:29 pm »
Hi i just test your module, and when i start example and click on one answer i have this :

A [Mrbot] : !textadventure 1 10
[Mrbot]: /tell Mrbot !help

Offline Gauche

  • BeBot Rookie
  • *
  • Posts: 16
  • Karma: +0/-0
Re: Text Adventure Module
« Reply #4 on: November 26, 2009, 05:16:42 pm »
I make some translation for my french guildies and i suppose i remove or translate one var because i haven't this : !textadventure filename 1 10

i have this :
!textadventure 1 10

Offline Gauche

  • BeBot Rookie
  • *
  • Posts: 16
  • Karma: +0/-0
Re: Text Adventure Module
« Reply #5 on: November 26, 2009, 05:35:36 pm »
After somme test if i whrite !textadventure filename 1 10 --> work

I just want to give automatically <filename> in the generated link

I post my code :

Code: [Select]
<?php
/*
 * Aventure RPG texte pour BeBot
 * Version: 1.1
 * Copyright (c) 2009 djkrose
 * Modification / traduction par Gauche
 * Change log:
 *   v1.1  - Traduction en français par Gauche pour la guilde Vigilance sur Ishtar.
 *   v1.0  - First Release version!
 *         - Fixed some formatting.
 *   v0.2  - Added possibility to have multiple choices with same target.
 *         - Removed chapter text in info blob.
 *         - Added possibility to include html tags
 *         - Added support for ##include=file.txt##
 *         - Added setting to allow lateral entrance from every chapter.
 *         - Added support to define lateral entrance to particular chapters using "-->" before the chapter number.
 *   v0.1  - First working version.  
 */

$rpgaventure = new RpgAventure($bot);

class 
RpgAventure extends BaseActiveModule {

private $AVENTURE_FILE_DIRECTORY "./rpgaventures";
private $MSG_ONLY_CLICK_ONCE "Un clic suffit! (soyez patient)";
private $progress = array();
private $adventures = array();
private $cooldown = array();
private $file_times = array();

function __construct(&$bot) {
parent::__construct($botget_class($this));

// Register commands
$this -> register_command('all''rpgaventure''GUEST');

// Create settings
$this -> bot -> core("settings") -> create("RpgAventure""TimeOut"5,
"Après combien de minutes le status du chapitre en cour doit être reset?""1;2;3;4;5;10;20;30;60");
$this -> bot -> core("settings") -> create("RpgAventure""CoolDown"5,
"Après combien de minutes les joueurs peuvent retenter une aventure?""1;2;3;4;5;10;20;30;60");
$this -> bot -> core("settings") -> create("RpgAventure""LateralEntrance"FALSE,
"Autoriser les joueurs a commencer depuis n'importe quel chapitre, avec la commande \"-->\" marker?");

// Set help texts
$this -> help['description'] = 'Permet aux utilisateurs de réaliser des RPG en mode texte.';
$this -> help['command']['rpgaventure <exemple>']="Commencer l'aventure RPG nommé <exemple>.";
$this -> help['notes'] = "Les admins peuvent modifier les paramétres sur les timers avec la commande !settings.";
}

function command_handler($name$msg$origin) {
// Process input command
if (preg_match('/^rpgaventure +([a-zA-ZöäüßÖÄÜ_-]+) +([0-9]+) +([0-9]+) *$/i'$msg$matches))
$reply $this->process_step($name$matches[1], (int)$matches[2], (int)$matches[3]);
elseif (preg_match('/^rpgaventure +([a-zA-ZöäüßÖÄÜ_-]+) *$/i'$msg$matches))
$reply $this->process_step($name$matches[1]);
else
$this->bot->send_help($name);
// Send reply via tell, if any
if ($reply)
$this->bot->send_tell($name$reply);
}

/**
 * Supprimer tous les éléments de $this->Temps dépassé.
 */
function clear_timed_out_progress() {
$time_out $this->bot->core("settings")->get("RpgAventure""TimeOut");
$time time();
foreach($this->progress as $key=>$value) {
if ($value['time_stamp'] + $time_out*60 $time)
unset($this->progress[$key]);
}
}

/**
 * Supprimer tous les éléments de $this->Temps de recharge dépassé.
 */
function clear_timed_out_cooldown() {
$time_out $this->bot->core("settings")->get("RpgAventure""CoolDown");
$time time();
foreach($this->cooldown as $key=>$value) {
if ($value $time_out*60 $time)
unset($this->cooldown[$key]);
}
}

/**
 * Rappel pour preg_replace_callback qui est appelée à chaque include trouvé dans le texte
 * de l'aventure. Attention risque de faille niveau sécurité! Ne jamais utiliser d'aventure non vérifié,
         * car la fonction include peut charger n'importe quel fichier!
 * 
 * @param $matches Utilisation de l'expression matches; le premier élément correspond
 *                 au nom du dossier relatif a l'aventure.
 * @return Retourne le contenu d'un fichier ou une chaîne vide en cas d'erreur.
 */
function include_callback($matches) {
$content = @file($this->AVENTURE_FILE_DIRECTORY."/".$matches[1]);
if ($content === FALSE)
return "";
else
return join(""$content);
}

/**
 * Makes sure, that the given adventure is properly parsed and ready to use in $this->aventures.
 * If it's not yet ready or if the source file has changed, it will be (re)parsed.
 *  
 * @param $aventure The text aventure name to parse. 
 * @return FALSE on any parsing errors
 */
function prepare_aventure($aventure) {
// Init some variables and values
$curradv =& $this->aventures[$aventure];
$aventure preg_replace('/[^a-zA-ZöäüßÖÄÜ_-]/'''$aventure); // Extra double security
$filename "$aventure.txt";
$filepath $this->AVENTURE_FILE_DIRECTORY."/".$filename;

// Don't re-load file if file hasn't been changed since last parsing
clearstatcache();
$file_time filemtime($filepath);
if (is_array($curradv) && $this->file_times[$filepath] >= $file_time)
return TRUE;
$this->file_times[$filepath] = $file_time;

// Load file contents
$curradv = array();
$lines = @file($filepath);
if ($lines === FALSE) {
$this->bot->log("RpgAventure""ERROR""Le fichier aventure $filename n'a pu être trouvé.");
return FALSE;
}

// Process includes; since included content can have line breaks, the new lines must be inserted in $lines.
for ($i=0$i<count($lines); $i++) {
$newlines preg_replace_callback('/##include=([^#]+)##/', array($this"include_callback"), $lines[$i], -1$count);
if ($count 0) {
$newlines preg_split('/(\r\n|\n|\r)/'$newlines);
$lines_before array_slice($lines0$i);
$lines_after  array_slice($lines$i+1);
$lines array_merge($lines_before$newlines$lines_after);
$i--;

}

// Parse file line by line
$chapter_mode FALSE;
$number 0;
foreach ($lines as $line) {
$number++;
$line trim($line);
// Ignore empty lines and comments
if (empty($line) || $line{0} == "#")
continue;
// Parse chapter number and its first text
if (preg_match('/^(-->)?\s*([0-9]+)\.\s*(.*)$/'$line$matches)) {
$current_chapter 2;
$curradv_chapter =& $curradv[(int)$matches[2]];
if (isset($curradv_chapter)) {
$this->bot->log("RpgAventure""ERROR""$filename:$number - 2 chapitres avec le même numéro trouvé!");
return FALSE;
}
$curradv_chapter['chapter_text'] = $matches[3];
// Set flag that lateral entrance to this chapter is allowed, if chapter line starts with "-->"
if ($matches[1] == "-->")
$curradv_chapter['lateral_entrance'] = TRUE;
$chapter_mode TRUE;
}
// Parse choice
elseif (preg_match('/^-\s*(.+?)\s*-->\s*([0-9]+)$/'$line$matches)) {
if (!isset($curradv_chapter)) {
$this->bot->log("RpgAventure""ERROR""$filename:$number - Choix non autorisé ici!");
return FALSE;
}
// Choices are collected in an extra array because we could have more than one choice with same target
$curradv_chapter['choices'][(int)$matches[2]][] = $matches[1];
$chapter_mode FALSE;
}
// Parse redirect
elseif (preg_match('/^-->\s*([0-9]+)$/'$line$matches)) {
if (!$chapter_mode) {
$this->bot->log("RpgAventure""ERROR""$filename:$number - Redirection non autorisé ici!");
return FALSE;
}
$curradv_chapter['redirect'] = (int)$matches[1];
unset($curradv_chapter); // unset the reference, not the actual array content! 
$chapter_mode FALSE;
}
// Parse additional chapter texts
elseif ($chapter_mode == TRUE) {
$curradv_chapter['chapter_text'] .= "\n$line";
}
else {
$this->bot->log("RpgAventure""ERROR""$filename:$number - Je ne sais pas gérer cette ligne!");
return FALSE;
}
}
// Check for existence of chapter 1
if (!isset($curradv[1])) {
$this->bot->log("RpgAventure""ERROR""$filename ne contient pas de chapitre 1. Ceci est obligatoire pour commencer l'aventure!");
return FALSE;
}
// Check if all choice targets exist as chapters
foreach ($curradv as $chapter_nr=>$chapter) {
if (!is_array($chapter["choices"]))
continue; 
foreach ($chapter["choices"] as $target=>$text) {
if (!isset($curradv[$target])) {
$this->bot->log("RpgAventure""ERROR""$filename - Chapitre $chapter_nr contient le choix $target, mais il n'existe pas!");
return FALSE;
}
}
}
$this->bot->log("RpgAventure""PARSE""RPG aventure $filename chargé.");
return TRUE;
}

/**
 * Verifier ou executer la prochainne (ou premiere) étape dans l'aventure donnée selon le
 * $chapter et $choice. La prochainne étape est annoncer directement a l'utilisateur, toutes erreur est          * retournée sous forme de chaîne et annoncé a l'utilisateur
 * Fonction est appelée directement par command_handler(..). Toutes les commandes nécessaires pour charger          * l'aventure, vérifier les erreurs et cheat, les cd et les temps dépassé se font ici aussi.
 *  
 * @param $name Le nom de l'utilisateur executant la commande rpgaventure
 * @param $aventure Le nom de l'aventure demandé en tant que chaîne
 * @param $chapter Le numéro du chapitre en cours, à savoir ou est l'utilisateur dans l'histoire en ce          * moment
 * @param $choice La cible choisit (numéro du prochain chapitre), ou l'utilisateur veut aller
 * @return Une chaîne envoyer a l'utilisateur en tell, ou rien du tout ce qui n'est pas plus mal^^. 
 */
function process_step($name$aventure$chapter FALSE$choice FALSE) {
// Init progress and aventures
$this->clear_timed_out_progress();
$this->clear_timed_out_cooldown();
$curradv  =& $this->aventures[$aventure];        // shortlink for the current adventure content
$currpro  =& $this->progress["$name:$aventure"];  // shortlink for the user's progress on current aventure
$currcool =& $this->cooldown["$name:$aventure"];  // shortlink for the user's cool down on current aventure
if (!$this->prepare_aventure($aventure))
return "##error##RPG aventure ##highlight##$aventure##end## n'a pu être trouvé ou a une erreur.##end##";

// Check if progress timed out
// (time out error in lateral entrance mode is not possible, because the user can start from everywhere)
$is_lateral_entrance_everywhere $this->bot->core("settings")->get("RpgAventure""LateralEntrance");
$is_lateral_entrance_here = ($choice !== FALSE && $curradv[(int)$choice]['lateral_entrance']);
if ($chapter !== FALSE && !isset($currpro) && !($is_lateral_entrance_everywhere || $is_lateral_entrance_here))
return "##error##Votre progression sur cette aventure a expirée ou vous l'avez terminé. Recommencer!##end##";

// Check if user tries to re-do steps or cheat
if ($chapter !== FALSE && isset($currpro) && $chapter != $currpro["chapter"]) {
if ($chapter == $currpro["last_chapter"] && $choice == $currpro["last_choice"])
    return; // assume double click and ignore it
else
return "##error##Votre choix n'est pas autorisé.Vous ne pouvez pas refaire une étape ou tricher!";
}

// Check if user tries to start over when not finished yet
if ($chapter === FALSE && isset($currpro)) {
if (!$currpro["last_chapter"])
return; // assume double click on start link, so we ignore it
else
return "##error##Vous ne pouvez pas recommencer, Vous n'avez pas fini!##end##"
}

// Check if choice is possible
if ($chapter !== FALSE && !isset($curradv[(int)$chapter]["choices"][(int)$choice]))
return "##error##Choix impossible!##end##";

// Check if user tries to start adventure when cooldown active
if (!isset($currpro) && isset($currcool))
return "##error##Vous ne pouvez recommencer cette aventure pour le moment, cool down encore actif##end##"

// If this adventure was started right now, activate the cool down
if (!isset($currpro))
$currcool time();

// Depending on users choice start with either chapter 1 or with his selection 
if ($choice === FALSE) {
$currpro["chapter"] = 1;
} else {
$currpro["last_chapter"] = (int)$chapter;
$currpro["last_choice"]  = (int)$choice;
$currpro["chapter"]      = (int)$choice;
}

unset($chapter); // does not contain current value any more
unset($choice);  // does not contain current value any more

// Make chapter text
$text $curradv[$currpro["chapter"]]["chapter_text"];

// Follow redirects
$i 0;
while (isset($curradv[$currpro["chapter"]]["redirect"])) {
$i++;
if ($i 50) {
$this->bot->log("RpgAventure""ERROR""Il semble que l'aventure $adventure a une redirection infini vers le chapitre {$currpro['chapter']}!");
$currpro NULL;
return "##error##Une erreur s'est produite lors du traitement de votre demande. S'il vous plaît informer Gauche!##end##";
}
$currpro["chapter"] = $curradv[$currpro["chapter"]]["redirect"];
}

// Add choice text as info blob
if (isset($curradv[$currpro["chapter"]]["choices"]) && count($curradv[$currpro["chapter"]]["choices"]) > 0) {
// $text_for_blob = preg_replace('/{([^{}]+)}/', '##highlight##$1##end##', $text);
// $text_for_blob = htmlspecialchars(strip_tags($text_for_blob));
// $text_for_blob = str_replace("\n", "<br>", $text_for_blob);
$info_blob  "##blob_title## ::::: RPG AVENTURE :::::<br></><br>";
// $info_blob .= "##blob_text##$text_for_blob<br>";
$info_blob .= "##blob_text##";
$i 0;
foreach ($curradv[$currpro["chapter"]]["choices"] as $target_id=>$target_texts) {
// One target can have multiple texts, which is why it is always an array of strings
foreach ($target_texts as $target_text) {
$i++;
$info_blob .= "$i. <a href='chatcmd:///tell {$this->bot->botname} <pre>rpgaventure " .
  "$adventure {$currpro['chapter']} $target_id'>" htmlspecialchars($target_text) . "</a><br>";
}
}
$info_blob .= "</><br>" $this->MSG_ONLY_CLICK_ONCE;
$info_blob str_replace("\"""&quot;"$info_blob);
$text preg_replace('/{([^{}]+)}/'"<a href=\"text://$info_blob\">\$1</a>"$text);
}

// Insert user's name for ##name##
$text str_replace("##name##"$name$text);

// Send multi-line text as multiple tells
$text explode("\n"$text);
foreach ($text as $msg)
$this->bot->send_tell($name$msg);

// If finished, clear progression
if (!is_array($curradv[$currpro["chapter"]]["choices"]) || count($curradv[$currpro["chapter"]]["choices"]) == 0)
$currpro NULL;
// If not finished, update time stamp
else
$currpro["time_stamp"] = time();
}

}

?>

Offline Jiheld

  • BeBot User
  • **
  • Posts: 53
  • Karma: +0/-0
Re: Text Adventure Module
« Reply #6 on: November 26, 2009, 09:20:01 pm »
the download link doesnt work

Offline Gauche

  • BeBot Rookie
  • *
  • Posts: 16
  • Karma: +0/-0
Re: Text Adventure Module
« Reply #7 on: November 27, 2009, 12:59:47 pm »
I found one error in my script i whrite this :    private $adventures = array(); i change in    private $aventures = array();

But i have the same error.

Edit : Ok i'm big noob... i define $aventures and i use $aventure...i haven't test but i'm sure it's ok^^
« Last Edit: November 27, 2009, 01:03:13 pm by Gauche »

Offline Runemy

  • BeBot Apprentice
  • ***
  • Posts: 97
  • Karma: +0/-0
    • Exalted [Age of Conan guild - Aquilonia EU]
Re: Text Adventure Module
« Reply #8 on: December 14, 2009, 08:38:21 pm »
Got this one up and working with no problems and I really like the potential of it, but is it possible to get all things happening inside the popup window instead of a combination of chat box and window?
Wood of Exalted
Age of Conan
Aquilonia - EU

 

* Recent Posts
[AoC] special char for items module by bitnykk
[February 09, 2024, 09:41:18 pm]


0.8.x updates for AoC by bitnykk
[January 30, 2024, 11:16:08 pm]


0.8.x updates for AO by bitnykk
[January 30, 2024, 11:15:37 pm]


BeBot still alive & kicking ! by bitnykk
[December 17, 2023, 12:58:44 am]


Bebot and Rasberry by bitnykk
[November 29, 2023, 11:04:14 pm]

* Who's Online
  • Dot Guests: 584
  • Dot Hidden: 0
  • Dot Users: 0

There aren't any users online.
* Forum Staff
bitnykk admin bitnykk
Administrator
Khalem admin Khalem
Administrator
WeZoN gmod WeZoN
Global Moderator
SimplePortal 2.3.7 © 2008-2024, SimplePortal