This repository has been archived on 2024-06-26. You can view files and clone it, but cannot push or open issues or pull requests.
eggdropDucks/Duck_Hunt.tcl
Pratyush Desai f230da60d8
Initialize
Signed-off-by: Pratyush Desai <pratyush.desai@liberta.casa>
2023-03-25 15:37:55 +05:30

4218 lines
240 KiB
Tcl
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

###############################################################################
#
# Duck Hunt
# v2.11 (11/04/2016) ©2015-2016 Menz Agitat
#
# IRC: irc.epiknet.org #boulets / #eggdrop
#
# Mes scripts sont téléchargeables sur http://www.eggdrop.fr
# Retrouvez aussi toute l'actualité de mes releases sur
# http://www.boulets.oqp.me/tcl/scripts/index.html
#
# Remerciements à Mon qui m'a donné l'idée de faire ce script, à Destiny pour le
# beta-testing intensif et pas mal d'idées, et à Frédéric pour aussi pas mal
# d'idées et la réalisation du background inclus (duck_background.png).
#
###############################################################################
#
# Description
#
# Duck Hunt est un FPS pour IRC.
# De temps en temps, un canard s'envole et les joueurs doivent l'abattre le plus
# rapidement possible.
#
# Veuillez vérifier que les paramètres de la section configuration ci-dessous
# vous conviennent, de même que les paramètres que contiennent le fichier
# Duck_Hunt.cfg.
#
###############################################################################
#
# Licence
#
# Cette création est mise à disposition selon le Contrat
# Attribution-NonCommercial-ShareAlike 3.0 Unported disponible en ligne
# http://creativecommons.org/licenses/by-nc-sa/3.0/ ou par courrier postal à
# Creative Commons, 171 Second Street, Suite 300, San Francisco, California
# 94105, USA.
# Vous pouvez également consulter la version française ici :
# http://creativecommons.org/licenses/by-nc-sa/3.0/deed.fr
#
###############################################################################
if {[::tcl::info::commands ::DuckHunt::uninstall] eq "::DuckHunt::uninstall"} { ::DuckHunt::uninstall }
if { [regsub -all {\.} [lindex $::version 0] ""] < 1620 } { putloglev o * "\00304\[Duck Hunt - erreur\]\003 La version de votre Eggdrop est\00304 ${::version}\003; Duck Hunt ne fonctionnera correctement que sur les Eggdrops version 1.6.20 ou supérieure." ; return }
if { [catch { package require Tcl 8.5 }] } { putloglev o * "\00304\[Duck Hunt - erreur\]\003 Duck Hunt nécessite que Tcl 8.5 (ou plus) soit installé pour fonctionner. Votre version actuelle de Tcl est\00304 ${::tcl_version}\003." ; return }
if { [catch { package require msgcat }] } { putloglev o * "\00304\[Duck Hunt - erreur\]\003 Duck Hunt nécessite le package msgcat pour fonctionner. Le chargement du script a été annulé." ; return }
namespace eval ::DuckHunt {
###############################################################################
### Configuration
###############################################################################
# Emplacement et nom du fichier de configuration.
variable config_file "scripts/duck_hunt/Duck_Hunt.cfg"
##### LANGUE ###############################################################
# Langue des messages du script ( fr = français / en = english )
# Remarque : Il s'agit d'un réglage global de votre Eggdrop; ce paramètre est
# mis ici pour vous en faciliter l'accès mais vous devez veiller à ce qu'il
# soit réglé de la même manière partout.
# Concrètement, vous ne pouvez pas définir la langue d'un script sur "fr" et
# celle d'un autre sur "en".
::msgcat::mclocale "fr"
# Emplacement des fichiers de langue.
variable language_files_directory "scripts/duck_hunt/language"
# Vous trouverez le reste des paramètres de configuration dans le fichier
# désigné par le paramètre config_file (voir plus haut).
###############################################################################
### Fin de la configuration
###############################################################################
#############################################################################
### Initialisation
#############################################################################
variable scriptname "Duck Hunt"
variable version "2.11.20160411"
setudef flag DuckHunt
setudef str DuckHunt-LastDuck
setudef str DuckHunt-PiecesOfBread
# Chargement des fichiers de langue.
::msgcat::mcload [file join $::DuckHunt::language_files_directory]
# Lecture de la configuration.
if { [file exists $::DuckHunt::config_file] } {
eval [list source $::DuckHunt::config_file]
} else {
# Message : "\00304\[%s - erreur\]\003 Le fichier de configuration n'a pas été trouvé à l'emplacement indiqué ( %s ). Le chargement du script est annulé."
putloglev o * [::msgcat::mc m180 $::DuckHunt::scriptname $::DuckHunt::config_file]
namespace delete ::DuckHunt
return
}
# Procédure de désinstallation : le script se désinstalle totalement avant
# chaque rehash ou à chaque relecture au moyen de la commande "source" ou
# autre.
proc uninstall {args} {
# Message : "Désallocation des ressources de %s..."
putlog [::msgcat::mc m0 $::DuckHunt::scriptname]
foreach binding [lsearch -inline -all -regexp [binds *[set ns [::tcl::string::range [namespace current] 2 end]]*] " \{?(::)?$ns"] {
unbind [lindex $binding 0] [lindex $binding 1] [lindex $binding 2] [lindex $binding 4]
}
foreach running_utimer [utimers] {
if { [::tcl::string::match "*[namespace current]::*" [lindex $running_utimer 1]] } { killutimer [lindex $running_utimer 2] }
}
foreach running_timer [timers] {
if { [::tcl::string::match "*[namespace current]::*" [lindex $running_timer 1]] } { killtimer [lindex $running_timer 2] }
}
if { [::tcl::dict::exists $::msgcat::Msgs [::msgcat::mclocale] [namespace current]] } {
::tcl::dict::unset ::msgcat::Msgs [::msgcat::mclocale] [namespace current]
}
if { $::DuckHunt::method == 2 } {
uplevel #0 [list trace remove execution *dcc:chanset leave ::DuckHunt::chanset_call]
uplevel #0 [list trace remove execution channel leave ::DuckHunt::chanset_call]
}
namespace delete ::DuckHunt
}
set ::DuckHunt::duck_sessions {}
if { $::DuckHunt::max_line_length <= 9 } {
set ::DuckHunt::max_line_length 10
}
set ::DuckHunt::report ""
array set ::DuckHunt::pending_transfers {}
set ::DuckHunt::post_init_done 0
}
###############################################################################
### Hook des commandes DCC et Tcl qui concernent l'activation et la
### désactivation d'un flag sur un chan afin de réajuster la planification des
### envols si method = 2.
###############################################################################
if { $::DuckHunt::method == 2 } {
uplevel #0 [list trace add execution *dcc:chanset leave ::DuckHunt::chanset_call]
uplevel #0 [list trace add execution channel leave ::DuckHunt::chanset_call]
proc ::DuckHunt::chanset_call {command errorcode result operation} {
if { !$errorcode } {
set lower_command [::tcl::string::tolower $command]
if { [lindex $lower_command 0] eq "*dcc:chanset" } {
lassign [lindex $command 3] chan flag
::DuckHunt::apply_planification_change $chan $flag
} elseif { [lindex $lower_command 1] eq "set" } {
lassign $command {} {} chan flag
::DuckHunt::apply_planification_change $chan $flag
}
}
}
proc ::DuckHunt::apply_planification_change {chan flag} {
if { [::tcl::string::equal -nocase $flag "+DuckHunt"] } {
::DuckHunt::plan_out_flights $chan
} elseif { [::tcl::string::equal -nocase $flag "-DuckHunt"] } {
if { [::tcl::dict::exists $::DuckHunt::binds_tables $chan] } {
foreach current_bind [::tcl::dict::get $::DuckHunt::binds_tables $chan] {
unbind {*}$current_bind
::tcl::dict::unset ::DuckHunt::binds_tables $chan
}
}
}
}
}
###############################################################################
### Chaque minute, on fouille les buissons pour voir si un canard ne s'y cache
### pas. (si method = 1)
###############################################################################
proc ::DuckHunt::check_bushes_for_duck {args} {
foreach chan [channels] {
if { [set num_pieces_of_bread [llength [channel get $chan DuckHunt-PiecesOfBread]]] != 0 } {
set extra_ducks [expr {$num_pieces_of_bread * 2}]
} else {
set extra_ducks 0
}
if {
([channel get $chan DuckHunt])
&& ([strftime "%H" [unixtime]] ni $::DuckHunt::duck_sleep_hours)
&& ([expr {int(rand() * (1440 - ([llength $::DuckHunt::duck_sleep_hours] * 60))) + 1}] <= [expr {$::DuckHunt::number_of_ducks_per_day + $extra_ducks}])
} then {
::DuckHunt::duck_soaring $chan - 0 - - - - - -
}
}
}
###############################################################################
### Réinitialisation / planification des heures d'envol (si method = 2).
###############################################################################
proc ::DuckHunt::plan_out_flights {args} {
if {
([llength $args] == 5)
|| ($args eq {})
} then {
set chans_to_process [channels]
} else {
set chans_to_process [lindex $args 0]
}
foreach chan $chans_to_process {
# On supprime les éventuelles planifications relatives à la journée
# précédente. On le fait aussi sur les chans où le flag DuckHunt n'est pas
# activé car il a pu être désactivé depuis la dernière planification.
if { [::tcl::dict::exists $::DuckHunt::binds_tables $chan] } {
foreach current_bind [::tcl::dict::get $::DuckHunt::binds_tables $chan] {
unbind {*}$current_bind
}
::tcl::dict::unset ::DuckHunt::binds_tables $chan
}
::tcl::dict::set ::DuckHunt::binds_tables $chan {}
# On planifie l'heure d'envol des canards.
if { [channel get $chan DuckHunt] } {
set hours_reference_list {00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23}
# On exclut les heures de sommeil des canards.
foreach sleep_hour $::DuckHunt::duck_sleep_hours {
set hours_reference_list [lsearch -all -inline -not -integer $hours_reference_list $sleep_hour]
}
set hours_list $hours_reference_list
if { [set num_pieces_of_bread [llength [channel get $chan DuckHunt-PiecesOfBread]]] != 0 } {
set ducks_per_day [expr {$::DuckHunt::number_of_ducks_per_day + $num_pieces_of_bread}]
} else {
set ducks_per_day $::DuckHunt::number_of_ducks_per_day
}
# En cas d'ajout de pain, on conserve l'heure la plus proche dans la
# planification actuelle pour la nouvelle planification, afin d'éviter
# que le changement fréquent de planification ait pour effet de raréfier
# les canards.
set time_to_keep ""
if {
([lindex $args 1] eq "bread_added")
|| (([lindex $args 1] eq "bread_expired")
&& ($num_pieces_of_bread > 0))
&& ($::DuckHunt::post_init_done)
} then {
set current_time [strftime "%H,%M" [unixtime]]
foreach scanned_time [lsort [::tcl::dict::get $::DuckHunt::planned_soarings $chan]] {
if { [regsub {:} $scanned_time {,}] > $current_time } {
set time_to_keep $scanned_time
break
}
}
}
::tcl::dict::set ::DuckHunt::planned_soarings $chan {}
for { set duck_number 1 } { $duck_number <= $ducks_per_day } { incr duck_number } {
if {
($time_to_keep ne "")
&& ($duck_number == 1)
} then {
lassign [split $time_to_keep ":"] chosen_hour chosen_minutes
} else {
set chosen_hour [lindex $hours_list [set hour_index [rand [llength $hours_list]]]]
set hours_list [lreplace $hours_list $hour_index $hour_index]
if { ![llength $hours_list] } {
set hours_list $hours_reference_list
}
set chosen_minutes [lindex {00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59} [rand 60]]
# Si par malchance l'heure sélectionnée est 00:00, le bind ne pourra pas
# se déclencher donc on décale d'une minute le cas échéant.
if {
($chosen_hour == 0)
&& ($chosen_minutes == 0)
} then {
set chosen_minutes 01
}
}
set current_bind [list time "-|-" "$chosen_minutes $chosen_hour * * *" [list ::DuckHunt::duck_soaring $chan - 0 -]]
# Si un bind existe déjà à cette heure, on modifie les minutes.
while { $current_bind in [::tcl::dict::get $::DuckHunt::binds_tables $chan] } {
set current_bind [list time "-|-" "[set chosen_minutes [lindex {00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59} [rand 60]]] $chosen_hour * * *" [list ::DuckHunt::duck_soaring $chan - 0 -]]
}
bind {*}$current_bind
::tcl::dict::lappend ::DuckHunt::binds_tables $chan $current_bind
::tcl::dict::lappend ::DuckHunt::planned_soarings $chan "${chosen_hour}:$chosen_minutes"
}
}
}
}
###############################################################################
### Un canard s'envole.
### $args reçoit 4 arguments pour un lancement manuel : {chan is_golden_duck is_fake_duck fake_duck_author}
### ou 9 pour un lancement auto : {chan is_golden_duck is_fake_duck fake_duck_author min hour day month year}
### Is_golden_duck peut valoir 0, 1 ou -
### Si is_golden_duck vaut - alors on décide aléatoirement.
### Is_fake_duck peut valoir 0 ou 1
### Fake_duck_author contient le nom du joueur qui a acheté le faux canard.
###############################################################################
proc ::DuckHunt::duck_soaring {args} {
lassign $args chan is_golden_duck is_fake_duck fake_duck_author
# Prévention contre les lancements multiples en cas de timer drift de
# l'Eggdrop.
if {
([set current_time [unixtime]] eq [channel get $chan DuckHunt-LastDuck])
&& ([llength $args] == 9)
} then {
return
} else {
# On décide s'il s'agit ou non d'un super-canard.
if { $is_golden_duck eq "-" } {
if { [expr {int(rand() * $::DuckHunt::number_of_ducks_per_day) + 1}] <= $::DuckHunt::approx_number_of_golden_ducks_per_day } {
set is_golden_duck 1
} else {
set is_golden_duck 0
}
}
if { $is_golden_duck } {
set HP [expr {int(rand() * ($::DuckHunt::golden_duck_max_HP - $::DuckHunt::golden_duck_min_HP + 1) + $::DuckHunt::golden_duck_min_HP)}]
} else {
set HP 1
}
# On avertit les joueurs qui possèdent un détecteur de canards.
::DuckHunt::read_database
if { [::tcl::dict::exists $::DuckHunt::player_data $chan] } {
set some_players_have_been_warned 0
foreach lower_nick [::tcl::dict::keys [::tcl::dict::get $::DuckHunt::player_data $chan]] {
if { [set item_index [lindex [::DuckHunt::get_item_info $lower_nick $chan "22"] 0]] != -1 } {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [lreplace [::DuckHunt::get_data $lower_nick $chan "items"] $item_index $item_index]
# On vérifie si le joueur n'a pas un transfert de stats en attente.
foreach pending_transfer_hash [array names ::DuckHunt::pending_transfers] {
lassign $::DuckHunt::pending_transfers($pending_transfer_hash) oldnick newnick
if {
([::tcl::string::tolower $oldnick] eq $lower_nick)
&& ([onchan $newnick $chan])
} then {
::DuckHunt::ckeck_for_pending_rename $chan $newnick [set lower_nick [::tcl::string::tolower $newnick]] $pending_transfer_hash
}
}
set some_players_have_been_warned 1
# Message : "%s > CANARD sur %s"
::DuckHunt::display_output quick NOTICE $lower_nick [::msgcat::mc m351 [::DuckHunt::get_data $lower_nick $chan "nick"] $chan]
}
}
if { $some_players_have_been_warned } {
::DuckHunt::write_database
}
}
::DuckHunt::purge_db_from_memory
if { $::DuckHunt::hl_prevention } {
# Construction d'un canard unique pour déjouer les tentatives
# d'automatisation (HL, scripts, ...)
# Texte : "-.,¸¸.-·°'`'°·-.,¸¸.-·°'`'°·"
set trail [::msgcat::mc m136]
set trail_length [::tcl::string::length $trail]
set quarter_trail_length [expr {int($trail_length / 4)}]
lappend trail_indexes [set index [rand $trail_length]]
lappend trail_indexes [set index [expr {($index + $quarter_trail_length) % $trail_length}]]
lappend trail_indexes [set index [expr {($index + $quarter_trail_length) % $trail_length}]]
lappend trail_indexes [set index [expr {($index + $quarter_trail_length) % $trail_length}]]
set trail_indexes [lsort -integer -decreasing $trail_indexes]
for { set counter 0 } { $counter <= 3 } { incr counter } {
set trail [::tcl::string::replace $trail [lindex $trail_indexes $counter] [lindex $trail_indexes $counter]]
}
# Texte : {"\\_O<" "\\_o<" "\\_Õ<" "\\_õ<" "\\_Ô<" "\\_ô<" "\\_Ö<" "\\_ö<" "\\_Ø<" "\\_ø<" "\\_Ò<" "\\_ò<" "\\_Ó<" "\\_ó<" "\\_0<" "\\_©<" "\\_@<" "\\_º<" "\\_°<" "\\_^<" (...)}
set duck [lindex [::msgcat::mc m137] [rand [llength [::msgcat::mc m137]]]]
# Texte : {"COIN" "COIN" "COIN" "COIN" "COIN" "KWAK" "KWAK" "KWAAK" "KWAAK" "KWAAAK" "KWAAAK" (...)}
set cry [lindex [::msgcat::mc m138] [rand [llength [::msgcat::mc m138]]]]
set output_string "\00314$trail\017 \002$duck\002 \00314$cry\017"
} else {
# Message : "\00314-.,¸¸.-·°'`'°·-.,¸¸.-·°'`'°·\017 \002\\_O<\002 \00314COIN\017"
set output_string [::msgcat::mc m135]
}
::DuckHunt::display_output now PRIVMSG $chan $output_string
if { [set num_pieces_of_bread [llength [channel get $chan DuckHunt-PiecesOfBread]]] != 0 } {
set utimer_ID [utimer [expr {$::DuckHunt::escape_time + ($num_pieces_of_bread * 20)}] [list ::DuckHunt::terminate_duck_session $chan 0]]
} else {
set utimer_ID [utimer $::DuckHunt::escape_time [list ::DuckHunt::terminate_duck_session $chan 0]]
}
# Format de duck_sessions : {utimer_ID unixtime_en_ms_heure_envol nombre_tirs_manqués super_canard pts_vie_restants pts_vie_total déjà_signalé faux_canard auteur_du_faux_canard}
::tcl::dict::lappend ::DuckHunt::duck_sessions $chan [list $utimer_ID [::tcl::clock::milliseconds] 0 $is_golden_duck $HP $HP 0 $is_fake_duck $fake_duck_author]
channel set $chan DuckHunt-LastDuck $current_time
if {
($::DuckHunt::hunting_logs)
&& ([llength $args] == 9)
} then {
if { $is_golden_duck } {
::DuckHunt::add_to_log $chan $current_time - - - - "golden_duck_soaring" 0 -
} elseif { $is_fake_duck } {
::DuckHunt::add_to_log $chan $current_time - - - - "fake_duck_soaring" 0 -
} else {
::DuckHunt::add_to_log $chan $current_time - - - - "soaring" 0 -
}
}
}
}
###############################################################################
### Un canard a été abattu ou s'est échappé.
###############################################################################
proc ::DuckHunt::terminate_duck_session {chan has_been_shot} {
if { [::tcl::dict::exists $::DuckHunt::duck_sessions $chan] } {
if { !$has_been_shot } {
lassign [lindex [::tcl::dict::get $::DuckHunt::duck_sessions $chan] 0] {} {} {} is_golden_duck {} {} {} is_fake_duck {}
if { [llength [::tcl::dict::get $::DuckHunt::duck_sessions $chan]] > 1 } {
if { $is_golden_duck } {
# Message : "Un super-canard s'échappe. \00314·°'`'°-.,¸¸.·°'`\003"
::DuckHunt::display_output now PRIVMSG $chan [::msgcat::mc m247]
} elseif { $is_fake_duck } {
# Message : "Un canard mécanique s'échappe. \00314·°'`'°-.,¸¸.·°'`\003"
::DuckHunt::display_output now PRIVMSG $chan [::msgcat::mc m353]
} else {
# Message : "Un canard s'échappe. \00314·°'`'°-.,¸¸.·°'`\003"
::DuckHunt::display_output now PRIVMSG $chan [::msgcat::mc m3]
}
} else {
if { $is_golden_duck } {
# Message : "Le super-canard s'échappe. \00314·°'`'°-.,¸¸.·°'`\003"
::DuckHunt::display_output now PRIVMSG $chan [::msgcat::mc m248]
} elseif { $is_fake_duck } {
# Message : "Le canard mécanique s'échappe. \00314·°'`'°-.,¸¸.·°'`\003"
::DuckHunt::display_output now PRIVMSG $chan [::msgcat::mc m354]
} else {
# Message : "Le canard s'échappe. \00314·°'`'°-.,¸¸.·°'`\003"
::DuckHunt::display_output now PRIVMSG $chan [::msgcat::mc m4]
}
if { $::DuckHunt::gun_hand_back_mode == 2 } {
::DuckHunt::hand_back_weapons $chan
}
}
if { $::DuckHunt::hunting_logs } {
if { $is_golden_duck } {
::DuckHunt::add_to_log $chan [unixtime] - - - - "golden_duck_escaped" 0 -
} elseif { $is_fake_duck } {
::DuckHunt::add_to_log $chan [unixtime] - - - - "fake_duck_escaped" 0 -
} else {
::DuckHunt::add_to_log $chan [unixtime] - - - - "escaped" 0 -
}
}
} else {
killutimer [lindex [::tcl::dict::get $::DuckHunt::duck_sessions $chan] 0 0]
}
::tcl::dict::set ::DuckHunt::duck_sessions $chan [lreplace [::tcl::dict::get $::DuckHunt::duck_sessions $chan] 0 0]
if { [::tcl::dict::get $::DuckHunt::duck_sessions $chan] eq {} } {
::tcl::dict::unset ::DuckHunt::duck_sessions $chan
}
}
}
###############################################################################
### !bang : Un joueur tire.
###############################################################################
proc ::DuckHunt::shoot {nick host hand chan arg} {
if {
(![channel get $chan DuckHunt])
|| ($hand in $::DuckHunt::blacklisted_handles)
|| (($::DuckHunt::antiflood == 1)
&& (([::DuckHunt::antiflood $nick $chan "nick" $::DuckHunt::shooting_cmd $::DuckHunt::flood_shoot])
|| ([::DuckHunt::antiflood $nick $chan "chan" "*" $::DuckHunt::flood_global])))
} then {
return
} else {
set lower_nick [::tcl::string::tolower $nick]
if { $::DuckHunt::preferred_display_mode == 1 } {
set output_method "PRIVMSG"
set output_target $chan
} else {
set output_method "NOTICE"
set output_target $nick
}
::DuckHunt::read_database
::DuckHunt::ckeck_for_pending_rename $chan $nick $lower_nick [md5 "$chan,$lower_nick"]
::DuckHunt::initialize_player $nick $lower_nick $chan
if { [::tcl::dict::exists $::DuckHunt::duck_sessions $chan] } {
# On note le cumul du temps de réaction du joueur.
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "cumul_reflex_time" [expr {[::DuckHunt::get_data $lower_nick $chan "cumul_reflex_time"] + [expr {[::tcl::clock::milliseconds] - [lindex [::tcl::dict::get $::DuckHunt::duck_sessions $chan] 0 1]}]}]
set num_ducks_in_flight [llength [::tcl::dict::get $::DuckHunt::duck_sessions $chan]]
} else {
set num_ducks_in_flight 0
}
set current_time [unixtime]
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "last_activity" $current_time
# Le joueur n'a pas d'arme (arme confisquée).
if { [::DuckHunt::get_data $lower_nick $chan "gun"] <= 0 } {
# Message : "%s > tu n'es pas armé."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m5 $nick]
# Le joueur a reçu un seau d'eau.
} elseif { [lindex [::DuckHunt::get_item_info $lower_nick $chan "16"] 0] != -1 } {
# Message : "%s > À cause de %s, tes vêtements sont trempés et tu ne peux pas chasser comme ça. Tu dois encore patienter pendant %s."
::DuckHunt::display_output quick $output_method $output_target [::msgcat::mc m332 $nick [lindex [::DuckHunt::get_item_info $lower_nick $chan "16"] 2] [::DuckHunt::adapt_time_resolution [expr {([lindex [::DuckHunt::get_item_info $lower_nick $chan "16"] 1] - $current_time) * 1000}] 0]]
::DuckHunt::purge_db_from_memory
return
# Le joueur a une arme.
} else {
set sand_effect_msg ""
set sabotage_effect_msg ""
set dazzle_effect_msg ""
lassign [::DuckHunt::get_level_and_grantings [::DuckHunt::get_data $lower_nick $chan "xp"]] level required_xp accuracy deflection defense jamming ammos_per_clip ammo_clips xp_miss xp_wild_fire xp_accident
# Le joueur a du sable dans son arme.
lassign [::DuckHunt::get_item_info $lower_nick $chan "15"] item_index {} author
if { $item_index != -1 } {
set jamming [expr {$jamming * 2}]
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [lreplace [::DuckHunt::get_data $lower_nick $chan "items"] $item_index $item_index]
# Texte : " \00304\[ensablé par %s\]\003"
set sand_effect_msg [::msgcat::mc m333 $author]
}
# Le joueur a graissé son arme.
if { [lindex [::DuckHunt::get_item_info $lower_nick $chan "6"] 0] != -1 } {
set jamming [expr {int($jamming / 2)}]
}
# L'arme du joueur a été sabotée.
lassign [::DuckHunt::get_item_info $lower_nick $chan "17"] item_index {} sabotage_author
if { $item_index != -1 } {
set has_been_sabotaged 1
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [lreplace [::DuckHunt::get_data $lower_nick $chan "items"] $item_index $item_index]
# Texte : " \00304\[sabotage par %s\]\003"
set sabotage_effect_msg [::msgcat::mc m337 $sabotage_author]
} else {
set has_been_sabotaged 0
}
# Le joueur possède une assurance responsabilité civile.
if { [set item_index [lindex [::DuckHunt::get_item_info $lower_nick $chan "19"] 0]] != -1 } {
set xp_accident [expr {int($xp_accident / 3)}]
# Message : " \00303\[assurance resp. civile\]\003"
set liability_insurance_msg [::msgcat::mc m343]
} else {
set liability_insurance_msg ""
}
# L'arme est enrayée.
if { [::DuckHunt::get_data $lower_nick $chan "jammed"] } {
# Message : "%s > \00314*CLAC*\003 \00304ARME ENRAYÉE\003"
::DuckHunt::display_output quick $output_method $output_target [::msgcat::mc m7 $nick]
::DuckHunt::purge_db_from_memory
return
# L'arme s'enraye.
} elseif {
($has_been_sabotaged)
|| ([expr {int(rand()*100)+1}] <= $jamming)
} then {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "jammed" 1
::DuckHunt::incr_data $lower_nick $chan "jammed_weapons" +1
# Message : "%s > \00314*CLAC*\003 Ton arme s'est enrayée, tu dois recharger pour la décoincer... \00314|\003 Mun. : \002%s\002 \00314|\003 Charg. : \002%s\002"
::DuckHunt::display_output quick $output_method $output_target "[::msgcat::mc m8 $nick [::DuckHunt::display_ammo $lower_nick $chan $ammos_per_clip] [::DuckHunt::display_clips $lower_nick $chan $ammo_clips]]${sabotage_effect_msg}$sand_effect_msg"
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - - "jam" 0 -
}
# L'arme du joueur a été sabotée et explose.
if {
($has_been_sabotaged)
&& ($::DuckHunt::kick_when_sabotaged)
} then {
if { $::DuckHunt::kick_method } {
# Message : "\002*BOOM*\002 Ton arme vient d'exploser à cause du sabotage de %s."
putserv "CS kick $chan $nick [::msgcat::mc m338 $sabotage_author]"
} elseif { ![isop $::nick $chan] } {
# Message : "\00314\[%s\]\003 \00304:::\003 Erreur : %s n'a pas pu être kické sur %s car je n'y suis ni halfopé, ni opé."
::DuckHunt::display_output loglev - - [::msgcat::mc m140 $::DuckHunt::scriptname $nick $chan]
} else {
# Message : "\002*BOOM*\002 Ton arme vient d'exploser à cause du sabotage de %s."
putkick $chan $nick [::msgcat::mc m338 $sabotage_author]
}
}
# L'arme ne s'enraye pas.
} else {
# L'arme n'est pas chargée et les munitions ne sont pas illimitées.
if {
([::DuckHunt::get_data $lower_nick $chan "current_ammo_clip"] == 0)
&& !($::DuckHunt::unlimited_ammo_per_clip)
} then {
::DuckHunt::incr_data $lower_nick $chan "empty_shots" +1
# Message : "%s > \00314*CLIC*\003 \00304CHARGEUR VIDE\003 \00314|\003 Mun. : \002%s\002 \00314|\003 Charg. : \002%s\002"
::DuckHunt::display_output quick $output_method $output_target [::msgcat::mc m6 $nick [::DuckHunt::display_ammo $lower_nick $chan $ammos_per_clip] [::DuckHunt::display_clips $lower_nick $chan $ammo_clips]]
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - - "empty_shot" 0 -
}
# L'arme est chargée.
} else {
# Le joueur possède un détecteur infrarouge et il n'y a pas de canard.
lassign [::DuckHunt::get_item_info $lower_nick $chan "8"] item_index expiration_date item_uses
if {
!([set duck_present [::tcl::dict::exists $::DuckHunt::duck_sessions $chan]])
&& ($item_index != -1)
} then {
# Message : "%s > \00314*CLIC*\003 Gâchette verrouillée."
::DuckHunt::display_output quick $output_method $output_target [::msgcat::mc m290 $nick]
::DuckHunt::decrement_item_uses $lower_nick $chan "8" $item_index $expiration_date $item_uses
::DuckHunt::write_database
::DuckHunt::purge_db_from_memory
return
}
if { !$::DuckHunt::unlimited_ammo_per_clip } {
::DuckHunt::incr_data $lower_nick $chan "current_ammo_clip" -1
}
set base_accuracy $accuracy
# Le joueur est ébloui.
lassign [::DuckHunt::get_item_info $lower_nick $chan "14"] item_index {} author
if { $item_index != -1 } {
set accuracy [expr {int($accuracy / 2)}]
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [lreplace [::DuckHunt::get_data $lower_nick $chan "items"] $item_index $item_index]
# Texte : " \00304\[ébloui par %s\]\003"
set dazzle_effect_msg [::msgcat::mc m334 $author]
}
# Le joueur a installé une lunette de visée sur son arme.
if { [set item_index [lindex [::DuckHunt::get_item_info $lower_nick $chan "7"] 0]] != -1 } {
set accuracy [expr {$accuracy + int((100 - $base_accuracy) / 3)}]
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [lreplace [::DuckHunt::get_data $lower_nick $chan "items"] $item_index $item_index]
}
set duck_fleed 0
# Le joueur rate son tir ou il n'y a aucun canard en vol.
if {
!($duck_present)
|| ([expr {int(rand()*100)+1}] > $accuracy)
} then {
::DuckHunt::incr_data $lower_nick $chan "missed_shots" +1
if { ![::tcl::info::exists previous_xp] } {
set previous_xp [::DuckHunt::get_data $lower_nick $chan "xp"]
}
::DuckHunt::incr_data $lower_nick $chan "xp" $xp_miss
if { $::DuckHunt::devoice_on_miss } {
pushmode $chan -v $nick
}
# Si des canards sont en vol, on incrémente le compteur de tirs
# manqués effectués en leur présence et on voit si certains ont
# atteint leur limite.
if { $duck_present } {
incr duck_fleed [::DuckHunt::ducks_scaring $chan $lower_nick]
set xp_wild_fire_msg ""
} else {
# Texte : "\[tir sauvage : %s xp\] "
set xp_wild_fire_msg [::msgcat::mc m9 $xp_wild_fire]
}
set chances_to_hit_someone_else [::DuckHunt::determine_chances_to_hit_someone_else $chan $duck_present]
set someone_has_been_hit 0
set confiscation_msg_sent 0
set xp_penalty_msg_sent 0
set ricochet_counter 0
set source_nick $nick
# La balle perdue touche un autre joueur.
while {
([expr {int(rand()*100)+1}] <= $chances_to_hit_someone_else)
&& ($ricochet_counter < $::DuckHunt::max_ricochets)
&& ([set victim [::DuckHunt::random_user $chan $source_nick]] ne "@nobody@")
} {
set someone_has_been_hit 1
set source_nick $victim
set lower_victim [::tcl::string::tolower $victim]
::DuckHunt::ckeck_for_pending_rename $chan $victim $lower_victim [md5 "$chan,$lower_victim"]
::DuckHunt::incr_data $lower_nick $chan "humans_shot" +1
if { $::DuckHunt::devoice_on_accident } {
pushmode $chan -v $nick
}
if { ![::tcl::info::exists previous_xp] } {
set previous_xp [::DuckHunt::get_data $lower_nick $chan "xp"]
}
::DuckHunt::incr_data $lower_nick $chan "xp" $xp_accident
::DuckHunt::initialize_player $victim $lower_victim $chan
::DuckHunt::incr_data $lower_victim $chan "bullets_received" +1
# Confiscation éventuelle de l'arme.
if {
($::DuckHunt::gun_confiscation_when_shooting_someone)
&& !($confiscation_msg_sent)
} then {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "gun" 0
::DuckHunt::incr_data $lower_nick $chan "confiscated_weapons" +1
# Texte : " \00304\[ARME CONFISQUÉE : accident de chasse\]\003"
set gun_confiscation1 [::msgcat::mc m10]
# Texte : " ainsi que son arme"
set gun_confiscation2 [::msgcat::mc m11]
} else {
set gun_confiscation1 ""
set gun_confiscation2 ""
}
# On n'affiche qu'une seule fois l'xp perdue pour tir raté/sauvage.
if { !$xp_penalty_msg_sent } {
if { !$duck_present } {
# Texte : "\[raté : %s xp\] "
set xp_penalty_msg "[::msgcat::mc m12 $xp_miss]$xp_wild_fire_msg"
set lost_xp [expr {abs($xp_miss) + abs($xp_wild_fire) + abs($xp_accident)}]
} else {
# Texte : "\[raté : %s xp\] "
set xp_penalty_msg "[::msgcat::mc m12 $xp_miss]"
set lost_xp [expr {abs($xp_miss) + abs($xp_accident)}]
}
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - - "accident" 1 -
}
} else {
set xp_penalty_msg ""
set lost_xp [expr {abs($xp_accident)}]
}
# La victime possède une assurance vie.
if { [set item_index [lindex [::DuckHunt::get_item_info $lower_victim $chan "18"] 0]] != -1 } {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_victim "items" [lreplace [::DuckHunt::get_data $lower_victim $chan "items"] $item_index $item_index]
::DuckHunt::incr_data $lower_victim $chan "xp" [set life_insurance_xp [expr {[lindex [::DuckHunt::get_level_and_grantings [::DuckHunt::get_data $lower_victim $chan "xp"]] 0] * 2}]]
# Message : " \00303\[assurance vie : +%s xp pour %s\]\003"
set life_insurance_msg [::msgcat::mc m340 $life_insurance_xp $victim]
# Message : " \00303\[assurance vie : +%s xp\]\003"
set life_insurance_msg2 [::msgcat::mc m341 $life_insurance_xp]
} else {
set life_insurance_msg ""
set life_insurance_msg2 ""
}
lassign [::DuckHunt::get_level_and_grantings [::DuckHunt::get_data $lower_victim $chan "xp"]] {} {} {} victim_deflection victim_defense {} {} {} {} {} {}
# La balle ricoche.
if { [expr {int(rand()*100)+1}] <= $victim_deflection } {
::DuckHunt::incr_data $lower_victim $chan "deflected_bullets" +1
incr ricochet_counter
set confiscation_msg_sent 1
set xp_penalty_msg_sent 1
# Message : "\00314*PIEWWW*\003 La balle de %s ricoche sur %s grâce à son modificateur de déflexion de %s%%. \00304%s\[accident : %s xp\]\003"
::DuckHunt::display_output quick PRIVMSG $chan "[::msgcat::mc m13 $nick $victim $victim_deflection $xp_penalty_msg $xp_accident]${gun_confiscation1}${dazzle_effect_msg}${liability_insurance_msg}$life_insurance_msg"
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick $lower_victim - "deflect" 1 -
}
# Un canard est touché par ricochet.
if {
($duck_present)
&& ([expr {int(rand()*100)+1}] <= $::DuckHunt::chances_to_ricochet_towards_duck)
} then {
::DuckHunt::hit_a_duck $nick $lower_nick $chan 1 $output_method $output_target
break
}
# L'autre joueur résiste.
} elseif { [expr {int(rand()*100)+1}] <= $victim_defense } {
set confiscation_msg_sent 1
set xp_penalty_msg_sent 1
# Message : "\00314*CHTOK*\003 %s reçoit la balle de %s mais accuse le coup grâce à son modificateur d'armure de %s%%. \00304%s\[accident : %s xp\]\003"
::DuckHunt::display_output quick PRIVMSG $chan "[::msgcat::mc m14 $victim $nick $victim_defense $xp_penalty_msg $xp_accident]${gun_confiscation1}${dazzle_effect_msg}${liability_insurance_msg}$life_insurance_msg"
if { $::DuckHunt::hunting_logs } {
if { $::DuckHunt::gun_confiscation_when_shooting_someone } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick $lower_victim - "hit" 1 -
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick $lower_victim - "confiscated" 0 -
} else {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick $lower_victim - "hit" 0 -
}
}
break
# L'autre joueur est abattu.
} else {
::DuckHunt::incr_data $lower_victim $chan "deaths" +1
set confiscation_msg_sent 1
set xp_penalty_msg_sent 1
if { $::DuckHunt::kick_when_shot } {
if { $::DuckHunt::kick_method } {
# Message : "\002*BANG*\002 Tu viens d'être victime d'un accident de chasse. %s s'en excuse... et perd %s pts d'xp."
putserv "CS kick $chan $victim [::msgcat::mc m15 $nick $lost_xp]${gun_confiscation2}$life_insurance_msg2"
} elseif { ![isop $::nick $chan] } {
# Message : "\00314\[%s\]\003 \00304:::\003 Erreur : %s n'a pas pu être kické sur %s car je n'y suis ni halfopé, ni opé."
::DuckHunt::display_output loglev - - [::msgcat::mc m140 $::DuckHunt::scriptname $victim $chan]
} else {
# Message : "\002*BANG*\002 Tu viens d'être victime d'un accident de chasse. %s s'en excuse... et perd %s pts d'xp."
putkick $chan $victim "[::msgcat::mc m15 $nick $lost_xp]${gun_confiscation2}$life_insurance_msg2"
}
}
# Message : "\00314*BANG*\003 \002\037xO\037'\002 %s vient de se faire descendre par %s par accident. \00304%s\[accident : %s xp\]\003"
::DuckHunt::display_output quick PRIVMSG $chan "[::msgcat::mc m16 $victim $nick $xp_penalty_msg $xp_accident]${gun_confiscation1}${dazzle_effect_msg}${liability_insurance_msg}$life_insurance_msg"
if { $::DuckHunt::hunting_logs } {
if { $::DuckHunt::gun_confiscation_when_shooting_someone } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick $lower_victim - "die" 1 -
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick $lower_victim - "confiscated" 0 -
} else {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick $lower_victim - "die" 0 -
}
}
break
}
}
# Il n'y a actuellement aucun canard en vol sur le chan.
if { !$duck_present } {
::DuckHunt::incr_data $lower_nick $chan "wild_shots" +1
if { ![::tcl::info::exists previous_xp] } {
set previous_xp [::DuckHunt::get_data $lower_nick $chan "xp"]
}
::DuckHunt::incr_data $lower_nick $chan "xp" $xp_wild_fire
if { $::DuckHunt::devoice_on_wild_fire } {
pushmode $chan -v $nick
}
# Confiscation éventuelle de l'arme.
if {
($::DuckHunt::gun_confiscation_on_wild_fire)
&& ([::DuckHunt::get_data $lower_nick $chan "gun"] == 1)
} then {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "gun" 0
::DuckHunt::incr_data $lower_nick $chan "confiscated_weapons" +1
# Texte : " \00304\[ARME CONFISQUÉE : tir sauvage\]\003"
set gun_confiscation [::msgcat::mc m17]
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - - "wild_fire" 1 -
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - - "confiscated" 0 -
}
} else {
set gun_confiscation ""
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - - "wild_fire" 0 -
}
}
# Si personne n'a été touché, c'est juste un tir sauvage.
if { !$someone_has_been_hit } {
# Message : "%s > Par chance tu as raté, mais tu visais qui au juste ? Il n'y a aucun canard dans le coin... \00304\[raté : %s xp\] \[tir sauvage : %s xp\]\003"
::DuckHunt::display_output quick $output_method $output_target "[::msgcat::mc m18 $nick $xp_miss $xp_wild_fire]${gun_confiscation}"
}
# Il y a au moins un canard en vol sur le chan.
} else {
# Si personne n'a été touché, c'est juste un tir manqué.
if { !$someone_has_been_hit } {
# Message : "%s > Raté. \00304\[raté : %s xp\]\003"
::DuckHunt::display_output quick $output_method $output_target "[::msgcat::mc m19 $nick $xp_miss]$dazzle_effect_msg"
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - - "miss" 0 -
}
}
}
# Kick pour tir non-autorisé en l'absence de canard.
if {
(!$duck_present)
&& ($::DuckHunt::kick_on_wild_fire)
} then {
if { $::DuckHunt::kick_method } {
# Message : "Tu vises qui là ? Je ne vois aucun canard dans le coin. \[%s xp\] \[%s xp\]"
putserv "CS kick $chan $nick [::msgcat::mc m20 $xp_miss $xp_wild_fire]"
} elseif { ![isop $::nick $chan] } {
# Message : "\00314\[%s\]\003 \00304:::\003 Erreur : %s n'a pas pu être kické sur %s car je n'y suis ni halfopé, ni opé."
::DuckHunt::display_output loglev - - [::msgcat::mc m140 $::DuckHunt::scriptname $nick $chan]
} else {
# Message : "Tu vises qui là ? Je ne vois aucun canard dans le coin. \[%s xp\] \[%s xp\] "
putkick $chan $nick [::msgcat::mc m20 $xp_miss $xp_wild_fire]
}
}
if {
([::tcl::info::exists previous_xp])
&& ([lindex [::DuckHunt::get_level_and_grantings $previous_xp] 0] > [lindex [::DuckHunt::get_level_and_grantings [::DuckHunt::get_data $lower_nick $chan "xp"]] 0])
} then {
# Message "%s est rétrogradé(e) au rang de chasseur niveau %s (%s)."
::DuckHunt::display_output quick PRIVMSG $chan [::msgcat::mc m2 $nick [set level [lindex [::DuckHunt::get_level_and_grantings [::DuckHunt::get_data $lower_nick $chan "xp"]] 0]] [::DuckHunt::lvl2rank $level]]
}
# Le joueur réussit son tir.
} else {
::DuckHunt::hit_a_duck $nick $lower_nick $chan 0 $output_method $output_target
incr num_ducks_in_flight -1
if { $::DuckHunt::successful_shots_also_scares_ducks } {
incr duck_fleed [::DuckHunt::ducks_scaring $chan $lower_nick]
}
}
# Le tir manqué a effrayé un ou plusieurs canards qui parviennent à
# s'échapper.
if { $duck_fleed > 1 } {
if { $duck_fleed == $num_ducks_in_flight } {
# Message : "Effrayés par tout ce bruit, tous les canards s'échappent. \00314·°'`'°-.,¸¸.·°'`\003"
::DuckHunt::display_output now PRIVMSG $chan [::msgcat::mc m21]
} else {
# Message : "Effrayés par tout ce bruit, %s canards s'échappent. \00314·°'`'°-.,¸¸.·°'`\003"
::DuckHunt::display_output now PRIVMSG $chan [::msgcat::mc m22 $duck_fleed]
}
for { set counter 1 } { $counter <= $duck_fleed } { incr counter } {
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time - - - - "frightened" 0 -
}
}
} elseif { $duck_fleed == 1 } {
if { $num_ducks_in_flight == 1 } {
# Message : "Effrayé par tout ce bruit, le canard s'échappe. \00314·°'`'°-.,¸¸.·°'`\003"
::DuckHunt::display_output now PRIVMSG $chan [::msgcat::mc m23]
} else {
# Message : "Effrayé par tout ce bruit, un canard s'échappe. \00314·°'`'°-.,¸¸.·°'`\003"
::DuckHunt::display_output now PRIVMSG $chan [::msgcat::mc m24]
}
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time - - - - "frightened" 0 -
}
}
}
}
::DuckHunt::recalculate_ammo_on_lvl_change $lower_nick $chan
::DuckHunt::write_database
}
::DuckHunt::purge_db_from_memory
}
return
}
###############################################################################
### Un joueur a tiré, on incrémente le degré de peur des canards.
###############################################################################
proc ::DuckHunt::ducks_scaring {chan lower_nick} {
set duck_fleed 0
# Si le joueur n'a pas installé de silencieux sur son arme...
if {
([::tcl::dict::exists $::DuckHunt::duck_sessions $chan])
&& ([lindex [::DuckHunt::get_item_info $lower_nick $chan "9"] 0] == -1)
} then {
for { set counter 0 } { $counter <= [llength [::tcl::dict::get $::DuckHunt::duck_sessions $chan]] -1 } { incr counter } {
set shots_in_presence [expr {[lindex [::tcl::dict::get $::DuckHunt::duck_sessions $chan] $counter 2] + 1}]
if {
($shots_in_presence == $::DuckHunt::shots_before_duck_flee)
&& !([lindex [::tcl::dict::get $::DuckHunt::duck_sessions $chan] $counter 3])
&& !([lindex [::tcl::dict::get $::DuckHunt::duck_sessions $chan] $counter 7])
} then {
killutimer [lindex [::tcl::dict::get $::DuckHunt::duck_sessions $chan] $counter 0]
::tcl::dict::set ::DuckHunt::duck_sessions $chan [lreplace [::tcl::dict::get $::DuckHunt::duck_sessions $chan] $counter $counter]
incr duck_fleed
} else {
::tcl::dict::set ::DuckHunt::duck_sessions $chan [lreplace [::tcl::dict::get $::DuckHunt::duck_sessions $chan] $counter $counter [list [lindex [::tcl::dict::get $::DuckHunt::duck_sessions $chan] $counter 0] [lindex [::tcl::dict::get $::DuckHunt::duck_sessions $chan] $counter 1] $shots_in_presence [lindex [::tcl::dict::get $::DuckHunt::duck_sessions $chan] $counter 3] [lindex [::tcl::dict::get $::DuckHunt::duck_sessions $chan] $counter 4] [lindex [::tcl::dict::get $::DuckHunt::duck_sessions $chan] $counter 5] [lindex [::tcl::dict::get $::DuckHunt::duck_sessions $chan] $counter 6] [lindex [::tcl::dict::get $::DuckHunt::duck_sessions $chan] $counter 7] [lindex [::tcl::dict::get $::DuckHunt::duck_sessions $chan] $counter 8]]]
}
}
if { [::tcl::dict::get $::DuckHunt::duck_sessions $chan] eq {} } {
::tcl::dict::unset ::DuckHunt::duck_sessions $chan
}
}
return $duck_fleed
}
###############################################################################
### Un canard a été touché.
###############################################################################
proc ::DuckHunt::hit_a_duck {nick lower_nick chan lucky_shot output_method output_target} {
lassign [lindex [::tcl::dict::get $::DuckHunt::duck_sessions $chan] 0] utimer_ID soaring_time shots_in_presence is_golden_duck remaining_HP total_HP already_signaled is_fake_duck fake_duck_author
# Les dégâts varient selon le type de munitions utilisées.
if { [lindex [::DuckHunt::get_item_info $lower_nick $chan 3] 0] != -1 } {
set damage 2
# Texte : "*BANG*"
set fire_sound [::msgcat::mc m427]
# Texte : " \00303\[mun. AP\]\003"
set ammo_effect_msg [::msgcat::mc m428]
} elseif { [lindex [::DuckHunt::get_item_info $lower_nick $chan 4] 0] != -1 } {
set damage 3
# Texte : "*BOUM*"
set fire_sound [::msgcat::mc m430]
# Texte : " \00303\[mun. expl.\]\003"
set ammo_effect_msg [::msgcat::mc m429]
} else {
set damage 1
# Texte : "*BANG*"
set fire_sound [::msgcat::mc m426]
set ammo_effect_msg ""
}
set current_time [unixtime]
# Il s'agit d'un super-canard.
if { $is_golden_duck } {
if { $remaining_HP == $total_HP } {
set first_shot_on_golden_duck 1
} else {
set first_shot_on_golden_duck 0
}
incr remaining_HP -$damage
# Le super-canard a été touché mais n'est pas mort.
if { $remaining_HP > 0 } {
::tcl::dict::set ::DuckHunt::duck_sessions $chan [lreplace [::tcl::dict::get $::DuckHunt::duck_sessions $chan] 0 0 [list $utimer_ID $soaring_time $shots_in_presence $is_golden_duck $remaining_HP $total_HP $already_signaled]]
if {
($::DuckHunt::preferred_display_mode == 2)
&& !($already_signaled)
} then {
# Message : "\00307\002* SUPER-CANARD DÉTECTÉ *\002\003"
::DuckHunt::display_output now PRIVMSG $chan [::msgcat::mc m259]
::tcl::dict::set ::DuckHunt::duck_sessions $chan [lreplace [::tcl::dict::get $::DuckHunt::duck_sessions $chan] 0 0 [list $utimer_ID $soaring_time $shots_in_presence $is_golden_duck $remaining_HP $total_HP 1]]
}
if {
($first_shot_on_golden_duck)
&& ($::DuckHunt::preferred_display_mode != 2)
} then {
# Message : "%s > %s Le canard a survécu ! Essaie encore. \002\\_O<\002 \00304\[vie -%s\]\003 \00307\002* SUPER-CANARD DÉTECTÉ *\002\003"
::DuckHunt::display_output now $output_method $output_target [::msgcat::mc m249 $nick $fire_sound $damage]
} else {
# Message : "%s > %s Le super-canard a survécu ! Essaie encore. \002\\_O<\002 \00304\[vie -%s\]\003"
::DuckHunt::display_output now $output_method $output_target [::msgcat::mc m271 $nick $fire_sound $damage]
}
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick - - - "hit_golden_duck" 0 -
}
return
}
set xp_won [expr {$::DuckHunt::base_xp_golden_duck * $total_HP}]
if { $lucky_shot } {
incr xp_won $::DuckHunt::xp_lucky_shot
}
} elseif { $is_fake_duck } {
set xp_won 0
} else {
set xp_won $::DuckHunt::xp_duck
if { $lucky_shot } {
incr xp_won $::DuckHunt::xp_lucky_shot
}
}
# Le joueur possède un trèfle à 4 feuilles.
lassign [::DuckHunt::get_item_info $lower_nick $chan "10"] item_index expiration_date bonus_xp
if { $item_index != -1 } {
incr xp_won $bonus_xp
# Texte : " \00304\[trèfle à 4 feuilles\]\003"
set clover_effect_msg [::msgcat::mc m369]
} else {
set clover_effect_msg ""
}
set shooting_time [expr {[::tcl::clock::milliseconds] - $soaring_time}]
::DuckHunt::terminate_duck_session $chan 1
if { $is_golden_duck } {
::DuckHunt::incr_data $lower_nick $chan "golden_ducks_shot" +1
}
::DuckHunt::incr_data $lower_nick $chan "ducks_shot" +1
if { [::DuckHunt::get_data $lower_nick $chan "xp"] + $xp_won >= [lindex [::DuckHunt::get_level_and_grantings [::DuckHunt::get_data $lower_nick $chan "xp"]] 1] } {
set level_gain 1
} else {
set level_gain 0
}
::DuckHunt::incr_data $lower_nick $chan "xp" $xp_won
if {
([expr {$shooting_time / 1000.0}] < [::DuckHunt::get_data $lower_nick $chan "best_time"])
|| ([::DuckHunt::get_data $lower_nick $chan "best_time"] == -1)
} then {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "best_time" [format %.3f [expr {$shooting_time / 1000.0}]]
}
if { $level_gain } {
# Message : " Tu deviens chasseur niveau %s (%s)."
set lvlup [::msgcat::mc m25 [set level [lindex [::DuckHunt::get_level_and_grantings [::DuckHunt::get_data $lower_nick $chan "xp"]] 0]] [::DuckHunt::lvl2rank $level]]
} else {
set lvlup ""
}
set human_readable_shooting_time [::DuckHunt::adapt_time_resolution $shooting_time 1]
# Le canard a-t-il droppé quelque chose ?
if { $::DuckHunt::drops_enabled } {
set has_dropped_something 0
set drop_msg ""
foreach {drop} {junk_item ammo clip AP_ammo explosive_ammo grease sight infrared_detector silencer sunglasses ducks_detector four_leaf_clover 10_xp 20_xp 30_xp 40_xp 50_xp 100_xp} {
if { [expr {int(rand()*1000) +1}] <= [set ::DuckHunt::chances_to_drop_[set drop]] } {
set has_dropped_something 1
# On s'arrête car on ne veut qu'un seul drop.
break
}
}
if { $has_dropped_something } {
lassign [::DuckHunt::get_level_and_grantings [::DuckHunt::get_data $lower_nick $chan "xp"]] {} {} {} {} {} {} default_ammos_in_clip default_ammo_clips_per_day {} {} {} {}
switch -- $drop {
"junk_item" {
# Message : "%s > En fouillant les buissons autour du canard, tu trouves... "
# Textes : "un canard en peluche." "un tas de plumes." "un canard en plastique pour le bain." "un chewing-gum mâchouillé." (...)
set drop_msg "[::msgcat::mc m393 $nick][set loot [lindex [::msgcat::mc m394] [rand [llength [::msgcat::mc m394]]]]]"
}
"ammo" {
if { [::DuckHunt::get_data $lower_nick $chan "current_ammo_clip"] < $default_ammos_in_clip } {
::DuckHunt::incr_data $lower_nick $chan "current_ammo_clip" +1
}
# Message : "%s > En fouillant les buissons autour du canard, tu trouves une balle supplémentaire !"
set drop_msg [::msgcat::mc m395 $nick]
# Texte : "balle supplémentaire"
set loot [::msgcat::mc m407]
}
"clip" {
if { [::DuckHunt::get_data $lower_nick $chan "remaining_ammo_clips"] < $default_ammo_clips_per_day } {
::DuckHunt::incr_data $lower_nick $chan "remaining_ammo_clips" +1
}
# Message : "%s > En fouillant les buissons autour du canard, tu trouves un chargeur supplémentaire !"
set drop_msg [::msgcat::mc m396 $nick]
# Texte : "chargeur supplémentaire"
set loot [::msgcat::mc m408]
}
"AP_ammo" {
if { [set item_index [lindex [::DuckHunt::get_item_info $lower_nick $chan "3"] 0]] != -1 } {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [lreplace [::DuckHunt::get_data $lower_nick $chan "items"] $item_index $item_index]
}
# Si le joueur possède déjà des munitions explosives, on les remplace.
if { [set item_index [lsearch -index 1 [::DuckHunt::get_data $lower_nick $chan "items"] "4"]] != -1 } {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [lreplace [::DuckHunt::get_data $lower_nick $chan "items"] $item_index $item_index]
}
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [concat [::DuckHunt::get_data $lower_nick $chan "items"] [list [list [expr {$current_time + 86400}] "3" "-"]]]
# Message : "%s > En fouillant les buissons autour du canard, tu trouves des munitions AP ! Les dégâts de ton arme sont doublés pendant 24h."
set drop_msg [::msgcat::mc m397 $nick]
# Texte : "munitions AP"
set loot [::msgcat::mc m409]
}
"explosive_ammo" {
if { [set item_index [lindex [::DuckHunt::get_item_info $lower_nick $chan "4"] 0]] != -1 } {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [lreplace [::DuckHunt::get_data $lower_nick $chan "items"] $item_index $item_index]
}
# Si le joueur possède déjà des munitions explosives, on les remplace.
if { [set item_index [lsearch -index 1 [::DuckHunt::get_data $lower_nick $chan "items"] "3"]] != -1 } {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [lreplace [::DuckHunt::get_data $lower_nick $chan "items"] $item_index $item_index]
}
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [concat [::DuckHunt::get_data $lower_nick $chan "items"] [list [list [expr {$current_time + 86400}] "4" "-"]]]
# Message : "%s > En fouillant les buissons autour du canard, tu trouves des munitions explosives ! Les dégâts de ton arme sont triplés pendant 24h."
set drop_msg [::msgcat::mc m398 $nick]
# Texte : "munitions explosives"
set loot [::msgcat::mc m410]
}
"grease" {
if { [set item_index [lindex [::DuckHunt::get_item_info $lower_nick $chan "6"] 0]] != -1 } {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [lreplace [::DuckHunt::get_data $lower_nick $chan "items"] $item_index $item_index]
}
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [concat [::DuckHunt::get_data $lower_nick $chan "items"] [list [list [expr {$current_time + 86400}] "6" "-"]]]
# Message : "%s > En fouillant les buissons autour du canard, tu trouves de la graisse pour ton arme ! Les risques d'enrayement de ton arme sont réduits de moitié pendant 24h."
set drop_msg [::msgcat::mc m399 $nick]
# Texte : "graisse"
set loot [::msgcat::mc m411]
}
"sight" {
if { [set item_index [lindex [::DuckHunt::get_item_info $lower_nick $chan "7"] 0]] != -1 } {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [lreplace [::DuckHunt::get_data $lower_nick $chan "items"] $item_index $item_index]
}
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [concat [::DuckHunt::get_data $lower_nick $chan "items"] [list [list "-" "7" "1"]]]
# Message : "%s > En fouillant les buissons autour du canard, tu trouves une lunette de visée pour ton arme ! La précision de ton prochain tir augmente de (100 - précision actuelle) / 3."
set drop_msg [::msgcat::mc m400 $nick]
# Texte : "lunette de visée"
set loot [::msgcat::mc m412]
}
"infrared_detector" {
if { [set item_index [lindex [::DuckHunt::get_item_info $lower_nick $chan "8"] 0]] != -1 } {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [lreplace [::DuckHunt::get_data $lower_nick $chan "items"] $item_index $item_index]
}
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [concat [::DuckHunt::get_data $lower_nick $chan "items"] [list [list [expr {$current_time + 86400}] "8" "6"]]]
# Message : "%s > En fouillant les buissons autour du canard, tu trouves un détecteur infrarouge ! La gâchette de ton arme sera bloquée s'il n'y a aucun canard dans les environs afin d'éviter le gaspillage de balles. Dure 24h pour 6 utilisations."
set drop_msg [::msgcat::mc m401 $nick]
# Texte : "détecteur infrarouge"
set loot [::msgcat::mc m413]
}
"silencer" {
if { [set item_index [lindex [::DuckHunt::get_item_info $lower_nick $chan "9"] 0]] != -1 } {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [lreplace [::DuckHunt::get_data $lower_nick $chan "items"] $item_index $item_index]
}
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [concat [::DuckHunt::get_data $lower_nick $chan "items"] [list [list [expr {$current_time + 86400}] "9" "-"]]]
# Message : "%s > En fouillant les buissons autour du canard, tu trouves un silencieux ! Tes tirs ne risquent plus d'effrayer les canards pendant 24h."
set drop_msg [::msgcat::mc m402 $nick]
# Texte : "silencieux"
set loot [::msgcat::mc m414]
}
"sunglasses" {
if { [set item_index [lindex [::DuckHunt::get_item_info $lower_nick $chan "11"] 0]] != -1 } {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [lreplace [::DuckHunt::get_data $lower_nick $chan "items"] $item_index $item_index]
}
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [concat [::DuckHunt::get_data $lower_nick $chan "items"] [list [list [expr {$current_time + 86400}] "11" "-"]]]
# Message : "%s > En fouillant les buissons autour du canard, tu trouves des lunettes de soleil ! Tu es protégé contre l'éblouissement pendant 24h et en plus, c'est la classe."
set drop_msg [::msgcat::mc m403 $nick]
# Texte : "lunettes de soleil"
set loot [::msgcat::mc m415]
}
"ducks_detector" {
if { [set item_index [lindex [::DuckHunt::get_item_info $lower_nick $chan "22"] 0]] != -1 } {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [lreplace [::DuckHunt::get_data $lower_nick $chan "items"] $item_index $item_index]
}
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [concat [::DuckHunt::get_data $lower_nick $chan "items"] [list [list "-" "22" "1"]]]
# Message : "%s > En fouillant les buissons autour du canard, tu trouves un détecteur de canards ! Tu seras averti par une notice lors de l'envol du prochain canard."
set drop_msg [::msgcat::mc m404 $nick]
# Texte : "détecteur de canards"
set loot [::msgcat::mc m416]
}
"four_leaf_clover" {
if { [set item_index [lindex [::DuckHunt::get_item_info $lower_nick $chan "10"] 0]] != -1 } {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [lreplace [::DuckHunt::get_data $lower_nick $chan "items"] $item_index $item_index]
}
set bonus_xp [expr {int(rand()*10) +1}]
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [concat [::DuckHunt::get_data $lower_nick $chan "items"] [list [list [expr {$current_time + 86400}] "10" $bonus_xp]]]
# Message : "%s > En fouillant les buissons autour du canard, tu trouves un trèfle à 4 feuilles +%s ! Chaque canard tué te rapportera %s %s pendant 24h."
# Textes "supplémentaire" "supplémentaires"
set drop_msg [::msgcat::mc m405 $nick $bonus_xp $bonus_xp [::DuckHunt::plural $bonus_xp "[::msgcat::mc m285] [::msgcat::mc m424]" "[::msgcat::mc m286] [::msgcat::mc m425]"]]
# Texte : "trèfle à 4 feuilles +%s"
set loot [::msgcat::mc m417 $bonus_xp]
}
"10_xp" {
::DuckHunt::incr_data $lower_nick $chan "xp" 10
# Message : "%s > En fouillant les buissons autour du canard, tu trouves un magazine de chasse. Sa lecture te rapporte %s %s ! \00303\[%s xp\]\003"
set drop_msg [::msgcat::mc m406 $nick "10" [::msgcat::mc m286] "10"]
# Texte : "magazine de chasse +%sxp"
set loot [::msgcat::mc m418 10]
}
"20_xp" {
::DuckHunt::incr_data $lower_nick $chan "xp" 20
# Message : "%s > En fouillant les buissons autour du canard, tu trouves un magazine de chasse. Sa lecture te rapporte %s %s ! \00303\[%s xp\]\003"
set drop_msg [::msgcat::mc m406 $nick "20" [::msgcat::mc m286] "20"]
# Texte : "magazine de chasse +%sxp"
set loot [::msgcat::mc m418 20]
}
"30_xp" {
::DuckHunt::incr_data $lower_nick $chan "xp" 30
# Message : "%s > En fouillant les buissons autour du canard, tu trouves un magazine de chasse. Sa lecture te rapporte %s %s ! \00303\[%s xp\]\003"
set drop_msg [::msgcat::mc m406 $nick "30" [::msgcat::mc m286] "30"]
# Texte : "magazine de chasse +%sxp"
set loot [::msgcat::mc m418 30]
}
"40_xp" {
::DuckHunt::incr_data $lower_nick $chan "xp" 40
# Message : "%s > En fouillant les buissons autour du canard, tu trouves un magazine de chasse. Sa lecture te rapporte %s %s ! \00303\[%s xp\]\003"
set drop_msg [::msgcat::mc m406 $nick "40" [::msgcat::mc m286] "40"]
# Texte : "magazine de chasse +%sxp"
set loot [::msgcat::mc m418 40]
}
"50_xp" {
::DuckHunt::incr_data $lower_nick $chan "xp" 50
# Message : "%s > En fouillant les buissons autour du canard, tu trouves un magazine de chasse. Sa lecture te rapporte %s %s ! \00303\[%s xp\]\003"
set drop_msg [::msgcat::mc m406 $nick "50" [::msgcat::mc m286] "50"]
# Texte : "magazine de chasse +%sxp"
set loot [::msgcat::mc m418 50]
}
"100_xp" {
::DuckHunt::incr_data $lower_nick $chan "xp" 100
# Message : "%s > En fouillant les buissons autour du canard, tu trouves un magazine de chasse. Sa lecture te rapporte %s %s ! \00303\[%s xp\]\003"
set drop_msg [::msgcat::mc m406 $nick "100" [::msgcat::mc m286] "100"]
# Texte : "magazine de chasse +%sxp"
set loot [::msgcat::mc m418 100]
}
}
}
}
if { !$lucky_shot } {
if { ![::tcl::dict::exists $::DuckHunt::duck_sessions $chan] } {
if { $is_golden_duck } {
# Message : "%s > %s Tu as eu le super-canard en %s, ce qui te fait un total de %s %s (dont %s %s) sur %s.%s \002\\_X<\002 \00314*COUAC*\003 \00303\[%s xp\]\003"
# Textes : "canard" "canards"
::DuckHunt::display_output quick PRIVMSG $chan "[::msgcat::mc m250 $nick $fire_sound $human_readable_shooting_time [::DuckHunt::get_data $lower_nick $chan "ducks_shot"] [::DuckHunt::plural [::DuckHunt::get_data $lower_nick $chan "ducks_shot"] [::msgcat::mc m27] [::msgcat::mc m28]] [::DuckHunt::get_data $lower_nick $chan "golden_ducks_shot"] [::DuckHunt::plural [::DuckHunt::get_data $lower_nick $chan "golden_ducks_shot"] [::msgcat::mc m274] [::msgcat::mc m275]] $chan $lvlup $xp_won]${clover_effect_msg}$ammo_effect_msg"
} elseif { $is_fake_duck } {
# Message : "%s > %s Tu as eu le canard mécanique en %s. Ce faux canard vous a été offert par %s. \002\\_X<\002 \00314*BZZzZzt*\003"
::DuckHunt::display_output quick PRIVMSG $chan [::msgcat::mc m356 $nick $fire_sound $human_readable_shooting_time $fake_duck_author]
} else {
# Message : "%s > %s Tu l'as eu en %s, ce qui te fait un total de %s %s sur %s.%s \002\\_X<\002 \00314*COUAC*\003 \00303\[%s xp\]\003"
::DuckHunt::display_output quick PRIVMSG $chan "[::msgcat::mc m26 $nick $fire_sound $human_readable_shooting_time [::DuckHunt::get_data $lower_nick $chan "ducks_shot"] [::DuckHunt::plural [::DuckHunt::get_data $lower_nick $chan "ducks_shot"] [::msgcat::mc m27] [::msgcat::mc m28]] $chan $lvlup $xp_won]$clover_effect_msg"
}
if { $::DuckHunt::gun_hand_back_mode == 2 } {
::DuckHunt::hand_back_weapons $chan
}
} else {
if { $is_golden_duck } {
# Message : "%s > %s Tu as eu un super-canard en %s, ce qui te fait un total de %s %s (dont %s %s) sur %s.%s \002\\_X<\002 \00314*COUAC*\003 \00303\[%s xp\]\003"
::DuckHunt::display_output quick PRIVMSG $chan "[::msgcat::mc m251 $nick $fire_sound $human_readable_shooting_time [::DuckHunt::get_data $lower_nick $chan "ducks_shot"] [::DuckHunt::plural [::DuckHunt::get_data $lower_nick $chan "ducks_shot"] [::msgcat::mc m27] [::msgcat::mc m28]] [::DuckHunt::get_data $lower_nick $chan "golden_ducks_shot"] [::DuckHunt::plural [::DuckHunt::get_data $lower_nick $chan "golden_ducks_shot"] [::msgcat::mc m274] [::msgcat::mc m275]] $chan $lvlup $xp_won]${clover_effect_msg}$ammo_effect_msg"
} elseif { $is_fake_duck } {
# Message : "%s > %s Tu as eu un canard mécanique en %s. Ce faux canard vous a été offert par %s. \002\\_X<\002 \00314*BZZzZzt*\003"
::DuckHunt::display_output quick PRIVMSG $chan [::msgcat::mc m357 $nick $fire_sound $human_readable_shooting_time $fake_duck_author]
} else {
# Message : "%s > %s Tu as eu un des canards en %s, ce qui te fait un total de %s %s sur %s.%s \002\\_X<\002 \00314*COUAC*\003 \00303\[%s xp\]\003"
::DuckHunt::display_output quick PRIVMSG $chan "[::msgcat::mc m155 $nick $fire_sound $human_readable_shooting_time [::DuckHunt::get_data $lower_nick $chan "ducks_shot"] [::DuckHunt::plural [::DuckHunt::get_data $lower_nick $chan "ducks_shot"] [::msgcat::mc m27] [::msgcat::mc m28]] $chan $lvlup $xp_won]$clover_effect_msg"
}
}
if { $::DuckHunt::hunting_logs } {
if { $is_golden_duck } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - $human_readable_shooting_time "shoot_golden_duck" 0 -
} else {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - $human_readable_shooting_time "shoot" 0 -
}
}
} else {
if { ![::tcl::dict::exists $::DuckHunt::duck_sessions $chan] } {
if { $is_golden_duck } {
# Message : "%s > %s Tu as eu le super-canard par ricochet en %s, ce qui te fait un total de %s %s (dont %s %s) sur %s.%s \002\\_X<\002 \00314*COUAC*\003 \00303\[%s xp\] \[lucky shot\]\003"
::DuckHunt::display_output quick PRIVMSG $chan "[::msgcat::mc m252 $nick $fire_sound $human_readable_shooting_time [::DuckHunt::get_data $lower_nick $chan "ducks_shot"] [::DuckHunt::plural [::DuckHunt::get_data $lower_nick $chan "ducks_shot"] [::msgcat::mc m27] [::msgcat::mc m28]] [::DuckHunt::get_data $lower_nick $chan "golden_ducks_shot"] [::DuckHunt::plural [::DuckHunt::get_data $lower_nick $chan "golden_ducks_shot"] [::msgcat::mc m274] [::msgcat::mc m275]] $chan $lvlup $xp_won]${clover_effect_msg}$ammo_effect_msg"
} elseif { $is_fake_duck } {
# Message : "%s > %s Tu as eu le canard mécanique par ricochet en %s. Ce faux canard vous a été offert par %s. \002\\_X<\002 \00314*BZZzZzt*\003"
::DuckHunt::display_output quick PRIVMSG $chan [::msgcat::mc m358 $nick $fire_sound $human_readable_shooting_time $fake_duck_author]
} else {
# Message : "%s > %s Tu l'as eu par ricochet en %s, ce qui te fait un total de %s %s sur %s.%s \002\\_X<\002 \00314*COUAC*\003 \00303\[%s xp\] \[lucky shot\]\003"
::DuckHunt::display_output quick PRIVMSG $chan "[::msgcat::mc m29 $nick $fire_sound $human_readable_shooting_time [::DuckHunt::get_data $lower_nick $chan "ducks_shot"] [::DuckHunt::plural [::DuckHunt::get_data $lower_nick $chan "ducks_shot"] [::msgcat::mc m27] [::msgcat::mc m28]] $chan $lvlup $xp_won]$clover_effect_msg"
}
if { $::DuckHunt::gun_hand_back_mode == 2 } {
::DuckHunt::hand_back_weapons $chan
}
} else {
if { $is_golden_duck } {
# Message : "%s > %s Tu as eu un super-canard par ricochet en %s, ce qui te fait un total de %s %s (dont %s %s) sur %s.%s \002\\_X<\002 \00314*COUAC*\003 \00303\[%s xp\] \[lucky shot\]\003"
::DuckHunt::display_output quick PRIVMSG $chan "[::msgcat::mc m253 $nick $fire_sound $human_readable_shooting_time [::DuckHunt::get_data $lower_nick $chan "ducks_shot"] [::DuckHunt::plural [::DuckHunt::get_data $lower_nick $chan "ducks_shot"] [::msgcat::mc m27] [::msgcat::mc m28]] [::DuckHunt::get_data $lower_nick $chan "golden_ducks_shot"] [::DuckHunt::plural [::DuckHunt::get_data $lower_nick $chan "golden_ducks_shot"] [::msgcat::mc m274] [::msgcat::mc m275]] $chan $lvlup $xp_won]${clover_effect_msg}$ammo_effect_msg"
} elseif { $is_fake_duck } {
# Message : "%s > %s Tu as eu un canard mécanique par ricochet en %s. Ce faux canard vous a été offert par %s. \002\\_X<\002 \00314*BZZzZzt*\003"
::DuckHunt::display_output quick PRIVMSG $chan [::msgcat::mc m359 $nick $fire_sound $human_readable_shooting_time $fake_duck_author]
} else {
# Message : "%s > %s Tu as eu un des canards par ricochet en %s, ce qui te fait un total de %s %s sur %s.%s \002\\_X<\002 \00314*COUAC*\003 \00303\[%s xp\] \[lucky shot\]\003"
::DuckHunt::display_output quick PRIVMSG $chan "[::msgcat::mc m156 $nick $fire_sound $human_readable_shooting_time [::DuckHunt::get_data $lower_nick $chan "ducks_shot"] [::DuckHunt::plural [::DuckHunt::get_data $lower_nick $chan "ducks_shot"] [::msgcat::mc m27] [::msgcat::mc m28]] $chan $lvlup $xp_won]$clover_effect_msg"
}
}
if { $::DuckHunt::hunting_logs } {
if { $is_golden_duck } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - $human_readable_shooting_time "dead_golden_duck" 0 -
} elseif { $is_fake_duck } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - $human_readable_shooting_time "dead_fake_duck" 0 -
} else {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - $human_readable_shooting_time "dead_duck" 0 -
}
}
}
if { $has_dropped_something } {
::DuckHunt::display_output help PRIVMSG $chan $drop_msg
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - - "drop" 0 $loot
}
}
if { $::DuckHunt::voice_when_duck_shot } {
pushmode $chan +v $nick
}
}
###############################################################################
### !reload : Recharge ou décoince son arme.
###############################################################################
proc ::DuckHunt::reload_gun {nick host hand chan arg} {
if {
(![channel get $chan DuckHunt])
|| ($hand in $::DuckHunt::blacklisted_handles)
|| (($::DuckHunt::antiflood == 1)
&& (([::DuckHunt::antiflood $nick $chan "nick" $::DuckHunt::reload_cmd $::DuckHunt::flood_reload])
|| ([::DuckHunt::antiflood $nick $chan "chan" "*" $::DuckHunt::flood_global])))
} then {
return
} else {
set lower_nick [::tcl::string::tolower $nick]
if { $::DuckHunt::preferred_display_mode == 1 } {
set output_method "PRIVMSG"
set output_target $chan
} else {
set output_method "NOTICE"
set output_target $nick
}
::DuckHunt::read_database
::DuckHunt::ckeck_for_pending_rename $chan $nick $lower_nick [md5 "$chan,$lower_nick"]
# Le joueur n'a pas de stats dans la db.
if {
!([::tcl::dict::exists $::DuckHunt::player_data $chan])
|| !([::tcl::dict::exists $::DuckHunt::player_data $chan $lower_nick])
} then {
lassign [::DuckHunt::get_level_and_grantings 1] {} {} {} {} {} {} ammos_per_clip ammo_clips {} {} {}
# Message : "%s > Ton arme n'a pas besoin d'être rechargée. \00314|\003 Munitions dans l'arme : \002%s/%s\002 \00314|\003 Chargeurs restants : \002%s/%s\002"
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m204 $nick $ammos_per_clip $ammos_per_clip $ammo_clips $ammo_clips]
# Le joueur a des stats dans la db.
} else {
set current_time [unixtime]
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "last_activity" $current_time
# Le joueur n'a pas d'arme (arme confisquée).
if { [::DuckHunt::get_data $lower_nick $chan "gun"] <= 0 } {
# Message : "%s > Tu n'es pas armé."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m5 $nick]
# Le joueur a une arme.
} else {
if { [::tcl::dict::exists $::DuckHunt::duck_sessions $chan] } {
# On note le cumul du temps de réaction du joueur.
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "cumul_reflex_time" [expr {[::DuckHunt::get_data $lower_nick $chan "cumul_reflex_time"] + [expr {[::tcl::clock::milliseconds] - [lindex [::tcl::dict::get $::DuckHunt::duck_sessions $chan] 0 1]}]}]
}
lassign [::DuckHunt::get_level_and_grantings [::DuckHunt::get_data $lower_nick $chan "xp"]] {} {} {} {} {} {} ammos_per_clip ammo_clips {} {} {}
# L'arme est enrayée.
if { [::DuckHunt::get_data $lower_nick $chan "jammed"] } {
# L'arme n'est pas chargée et les munitions par chargeur ne sont pas
# illimitées.
if {
!([::DuckHunt::get_data $lower_nick $chan "current_ammo_clip"])
&& !($::DuckHunt::unlimited_ammo_per_clip)
} then {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "jammed" 0
# Le joueur n'a plus de chargeurs en réserve et le nombre de chargeurs
# n'est pas illimité.
if {
(![::DuckHunt::get_data $lower_nick $chan "remaining_ammo_clips"])
&& !($::DuckHunt::unlimited_ammo_clips)
} then {
# Message : "%s > \00314*Crr..CLIC*\003 Tu décoinces ton arme mais tu es à court de munitions. \00314|\003 Mun. : \002%s\002 \00314|\003 Charg. : \002%s\002"
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m31 $nick [::DuckHunt::display_ammo $lower_nick $chan $ammos_per_clip] [::DuckHunt::display_clips $lower_nick $chan $ammo_clips]]
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - - "unjam" 0 -
}
# Lejoueur a encore des chargeurs en réserve.
} else {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "current_ammo_clip" $ammos_per_clip
if { !$::DuckHunt::unlimited_ammo_clips } {
::DuckHunt::incr_data $lower_nick $chan "remaining_ammo_clips" -1
}
# Message : "%s > \00314*Crr..CLIC*\003 Tu décoinces et recharges ton arme. \00314|\003 Mun. : \002%s\002 \00314|\003 Charg. : \002%s\002"
::DuckHunt::display_output quick $output_method $output_target [::msgcat::mc m32 $nick [::DuckHunt::display_ammo $lower_nick $chan $ammos_per_clip] [::DuckHunt::display_clips $lower_nick $chan $ammo_clips]]
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - - "unjam_reload" 0 -
}
}
# L'arme est déjà chargée.
} else {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "jammed" 0
# Message : "%s > \00314*Crr..CLIC*\003 Tu décoinces ton arme. \00314|\003 Mun. : \002%s\002 \00314|\003 Charg. : \002%s\002"
::DuckHunt::display_output quick $output_method $output_target [::msgcat::mc m33 $nick [::DuckHunt::display_ammo $lower_nick $chan $ammos_per_clip] [::DuckHunt::display_clips $lower_nick $chan $ammo_clips]]
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - - "unjam" 0 -
}
}
# L'arme n'est pas chargée et les munitions par chargeur ne sont pas
# illimitées.
} elseif {
!([::DuckHunt::get_data $lower_nick $chan "current_ammo_clip"])
&& !($::DuckHunt::unlimited_ammo_per_clip)
} then {
# Le joueur n'a plus de chargeurs en réserve et le nombre de chargeurs
# n'est pas illimité.
if {
!([::DuckHunt::get_data $lower_nick $chan "remaining_ammo_clips"])
&& !($::DuckHunt::unlimited_ammo_clips)
} then {
# Message : "%s > Tu es à court de munitions. \00314|\003 Mun. : \002%s\002 \00314|\003 Charg. : \002%s\002"
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m34 $nick [::DuckHunt::display_ammo $lower_nick $chan $ammos_per_clip] [::DuckHunt::display_clips $lower_nick $chan $ammo_clips]]
# Le joueur a encore des chargeurs en réserve.
} else {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "current_ammo_clip" $ammos_per_clip
if { !$::DuckHunt::unlimited_ammo_clips } {
::DuckHunt::incr_data $lower_nick $chan "remaining_ammo_clips" -1
}
# Message : "%s > \00314*CLAC CLAC*\003 Tu recharges. \00314|\003 Mun. : \002%s\002 \00314|\003 Charg. : \002%s\002"
::DuckHunt::display_output quick $output_method $output_target [::msgcat::mc m35 $nick [::DuckHunt::display_ammo $lower_nick $chan $ammos_per_clip] [::DuckHunt::display_clips $lower_nick $chan $ammo_clips]]
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - - "reload" 0 -
}
}
# L'arme n'a pas besoin d'être rechargée ou décoincée.
} else {
# Message : "%s > Ton arme n'a pas besoin d'être rechargée. \00314|\003 Munitions dans l'arme : \002%s\002 \00314|\003 Chargeurs restants : \002%s\002"
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m36 $nick [::DuckHunt::display_ammo $lower_nick $chan $ammos_per_clip] [::DuckHunt::display_clips $lower_nick $chan $ammo_clips]]
}
::DuckHunt::write_database
}
}
::DuckHunt::purge_db_from_memory
}
return
}
###############################################################################
### !lastduck : Affiche l'heure du dernier envol de canard.
###############################################################################
proc ::DuckHunt::pub_show_last_duck {nick host hand chan arg} {
if {
(![channel get $chan DuckHunt])
|| ($hand in $::DuckHunt::blacklisted_handles)
|| (($::DuckHunt::antiflood == 1)
&& (([::DuckHunt::antiflood $nick $chan "nick" $::DuckHunt::lastduck_pub_cmd $::DuckHunt::flood_lastduck])
|| ([::DuckHunt::antiflood $nick $chan "chan" "*" $::DuckHunt::flood_global])))
} then {
return
} else {
if { $::DuckHunt::preferred_display_mode == 1 } {
set output_method "PRIVMSG"
set output_target $chan
} else {
set output_method "NOTICE"
set output_target $nick
}
if { [channel get $chan DuckHunt-LastDuck] eq "" } {
# Message : "Aucun envol de canard n'a été enregistré sur %s."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m146 $chan]
} else {
# Message : "Le dernier canard a été aperçu il y a %s."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m144 [::DuckHunt::adapt_time_resolution [expr {([unixtime] - [channel get $chan DuckHunt-LastDuck]) * 1000}] 0]]
}
}
}
proc ::DuckHunt::msg_show_last_duck {nick host hand arg} {
if {
([matchattr $hand $::DuckHunt::lastduck_msg_auth [::DuckHunt::fix_chan_case [set chan [lindex [split $arg] 0]]]])
&& !($hand in $::DuckHunt::blacklisted_handles)
} then {
if {
([set arg [::tcl::string::trim $arg]] eq "")
|| ([llength [set arg [split $arg]]] != 1)
} then {
# Message : "\037Syntaxe\037 : \002%s\002 \00314<\003chan\00314>\003 \00307|\003 Affiche le temps écoulé depuis le dernier envol de canard sur le chan spécifié."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m145 $::DuckHunt::lastduck_msg_cmd]
} else {
if { ![validchan $chan] } {
# Message : "\00304:::\003 Erreur : %s n'est pas un chan valide."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m75 $chan]
} elseif { ![channel get $chan DuckHunt] } {
# Message : "\00304:::\003 Erreur : %s n'est pas activé sur %s."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m76 $::DuckHunt::scriptname $chan]
} elseif { [channel get $chan DuckHunt-LastDuck] eq "" } {
# Message : "Aucun envol de canard n'a été enregistré sur %s."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m146 $chan]
} else {
# Message : "Le dernier canard sur %s a été aperçu il y a %s."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m147 $chan [::DuckHunt::adapt_time_resolution [expr {([unixtime] - [channel get $chan DuckHunt-LastDuck]) * 1000}] 0]]
}
}
}
}
###############################################################################
### !duckstats [nick] : Affiche ses stats ou celles d'un autre.
###############################################################################
proc ::DuckHunt::display_stats {nick host hand chan arg} {
if {
(![channel get $chan DuckHunt])
|| ($hand in $::DuckHunt::blacklisted_handles)
|| (($::DuckHunt::antiflood == 1)
&& (([::DuckHunt::antiflood $nick $chan "nick" $::DuckHunt::stat_cmd $::DuckHunt::flood_stats])
|| ([::DuckHunt::antiflood $nick $chan "chan" "*" $::DuckHunt::flood_global])))
} then {
return
} else {
if { [set arg [::tcl::string::trim $arg]] ne "" } {
set target $arg
set lower_target [::tcl::string::tolower $arg]
} else {
set target $nick
set lower_target [::tcl::string::tolower $nick]
}
::DuckHunt::read_database
::DuckHunt::ckeck_for_pending_rename $chan $target $lower_target [md5 "$chan,$lower_target"]
foreach varname {gun jammed current_ammo_clip remaining_ammo_clips xp ducks_shot golden_ducks_shot missed_shots empty_shots humans_shot wild_shots bullets_received deflected_bullets deaths confiscated_weapons jammed_weapons best_time cumul_reflex_time items} {
set $varname [::DuckHunt::get_data $lower_target $chan $varname]
}
# Aucune entrée n'existe à ce nom dans la base de données, on prend les
# valeurs par défaut.
if {
!([::tcl::dict::exists $::DuckHunt::player_data $chan])
|| !([::tcl::dict::exists $::DuckHunt::player_data $chan $lower_target])
} then {
lassign [::DuckHunt::get_level_and_grantings 0] level required_xp accuracy deflection defense jamming ammos_per_clip ammo_clips {} {} {}
# Il existe une entrée pour ce joueur dans la base de données.
} else {
lassign [::DuckHunt::get_level_and_grantings [::DuckHunt::get_data $lower_target $chan "xp"]] level required_xp accuracy deflection defense jamming ammos_per_clip ammo_clips {} {} {}
}
set rank [::DuckHunt::lvl2rank $level]
set neutralized_bullets [expr {$bullets_received - $deaths - $deflected_bullets}]
set reliability [expr {100 - $jamming}]
# Le joueur a graissé son arme.
if { [lindex [::DuckHunt::get_item_info $lower_target $chan "6"] 0] != -1 } {
append reliability_modifier "\00303+[expr {int((100 - $reliability) / 2)}]%\003"
} else {
append reliability_modifier ""
}
# Le joueur a du sable dans son arme.
if { [lindex [::DuckHunt::get_item_info $lower_target $chan "15"] 0] != -1 } {
append reliability_modifier "\00304-[expr {int($reliability / 2)}]%\003"
} else {
append reliability_modifier ""
}
if { $best_time == -1 } {
set best_time "-"
} else {
set best_time [::DuckHunt::adapt_time_resolution [::tcl::string::map {"." ""} $best_time] 1]
}
if { $ducks_shot != 0 } {
set average_reflex_time [::DuckHunt::adapt_time_resolution [::tcl::string::map {"." ""} [format "%.3f" [expr {($cumul_reflex_time / 1000.0) / $ducks_shot}]]] 1]
} else {
set average_reflex_time "-"
}
if { $jammed == 1 } {
# Texte : "\00304oui\003"
set jammed [::msgcat::mc m37]
} else {
# Texte : "non"
set jammed [::msgcat::mc m38]
}
if { $gun <= 0 } {
# Texte : "\00304oui\003"
set confiscated [::msgcat::mc m37]
} else {
# Texte : "non"
set confiscated [::msgcat::mc m38]
}
set xp_to_lvlup [expr {$required_xp - $xp}]
set total_fired_ammo [expr {$ducks_shot + $missed_shots}]
if { $total_fired_ammo != 0 } {
set effective_accuracy "[expr {(100 * $ducks_shot) / $total_fired_ammo}]%"
} else {
set effective_accuracy "-"
}
# Le joueur est ébloui.
if { [set item_index [lindex [::DuckHunt::get_item_info $lower_target $chan "14"] 0]] != -1 } {
append accuracy_modifier "\00304-[expr {int($accuracy / 2)}]%\003"
} else {
append accuracy_modifier ""
}
# Le joueur a installé une lunette de visée sur son arme.
if { [set item_index [lindex [::DuckHunt::get_item_info $lower_target $chan "7"] 0]] != -1 } {
append accuracy_modifier "\00303+[expr {int((100 - $accuracy) / 3)}]%\003"
} else {
append accuracy_modifier ""
}
set items_list ""
set effects_list ""
# Textes : "Mun. AP" "Mun. expl." "Graisse" "Lunette de visée" "Détect. infrarouge" "Silencieux" "Trèfle à 4 feuilles" "Lunettes de soleil" "Ass. vie" "Ass. resp. civile" "Détect. canards" "Ébloui" "Sable" "Trempé" "Saboté"
set item_names [list 3 [::msgcat::mc m370] 4 [::msgcat::mc m371] 6 [::msgcat::mc m372] 7 [::msgcat::mc m373] 8 [::msgcat::mc m374] 9 [::msgcat::mc m375] 10 [::msgcat::mc m376] 11 [::msgcat::mc m377] 14 [::msgcat::mc m381] 15 [::msgcat::mc m382] 16 [::msgcat::mc m383] 17 [::msgcat::mc m384] 18 [::msgcat::mc m378] 19 [::msgcat::mc m379] 22 [::msgcat::mc m380]]
foreach item $items {
if { [set item_id [lindex $item 1]] in {3 4 6 7 8 9 10 11 18 19 22} } {
lappend items_list [::tcl::dict::get $item_names $item_id]
} elseif { $item_id in {14 15 16 17} } {
lappend effects_list [::tcl::dict::get $item_names $item_id]
}
}
if { $items_list ne "" } {
# Texte : "\00307\002 \[\037Inventaire\037\]\002\003 %s"
set items_list [::msgcat::mc m385 [join $items_list " \00314/\003 "]]
}
if { $effects_list ne "" } {
# Texte : "\00307\002 \[\037Effets\037\]\002\003 %s"
set effects_list [::msgcat::mc m386 [join $effects_list " \00314/\003 "]]
}
set karma [::DuckHunt::calculate_karma $wild_shots $humans_shot $ducks_shot 0]
# Message : "\00307\002\[\037Arme\037\]\002\003 mun. : %s \00307|\003 charg. : %s \00307|\003 enray. : %s (%s fois) \00307|\003 confisq. : %s (%s fois)\00307\002 \[\037Profil\037\]\002\003 %s XP \00307|\003 lvl %s (%s) / encore %s %s d'XP avant lvl sup. \00307|\003 karma : %s \00307\002\[\037Stats\037\]\002\003 précision théor. : %s%%%s \00307|\003 effic. tirs : %s \00307|\003 fiabilité arme : %s%%%s \00307|\003 armure : %s%% \00307|\003 déflexion : %s%%\n\00307\002\[\037Tableau de chasse\037\]\002\003 meill. tps. : %s \00307|\003 tps. réact. moyen : %s \00307|\003 %s %s (dont %s %s) \00307|\003 %s %s \00307|\003 %s %s \00307|\003 %s %s \00307|\003 %s %s \00307|\003 %s %s \00307\002\[\037Accidents\037\]\002\003 reçu %s %s dont %s %s, %s %s et %s %s. "
# Textes : "pt" "pts" "canard" "canards" "tir manqué" "tirs manqués" "accident" "accidents" "tir à vide" "tirs à vide" "tir sauvage" "tirs sauvages" "mun. utilis." "mun. utilis." "balle perdue" "balles perdues" "léthale" "léthales" "a ricoché" "ont ricoché" "a été encaissée" "ont été encaissées"
::DuckHunt::display_output help NOTICE $nick "[::msgcat::mc m42 [::DuckHunt::display_ammo $lower_target $chan $ammos_per_clip] [::DuckHunt::display_clips $lower_target $chan $ammo_clips] $jammed $jammed_weapons $confiscated $confiscated_weapons [::DuckHunt::colorize_value $xp] $level $rank $xp_to_lvlup [::DuckHunt::plural [expr {$required_xp - $xp}] [::msgcat::mc m43] [::msgcat::mc m44]] $karma $accuracy $accuracy_modifier $effective_accuracy $reliability $reliability_modifier $defense $deflection $best_time $average_reflex_time $ducks_shot [::DuckHunt::plural $ducks_shot [::msgcat::mc m45] [::msgcat::mc m46]] $golden_ducks_shot [::DuckHunt::plural $golden_ducks_shot [::msgcat::mc m274] [::msgcat::mc m275]] $missed_shots [::DuckHunt::plural $missed_shots [::msgcat::mc m47] [::msgcat::mc m48]] $humans_shot [::DuckHunt::plural $humans_shot [::msgcat::mc m49] [::msgcat::mc m50]] $empty_shots [::DuckHunt::plural $empty_shots [::msgcat::mc m51] [::msgcat::mc m52]] $wild_shots [::DuckHunt::plural $wild_shots [::msgcat::mc m53] [::msgcat::mc m54]] $total_fired_ammo [::DuckHunt::plural $total_fired_ammo [::msgcat::mc m55] [::msgcat::mc m56]] $bullets_received [::DuckHunt::plural $bullets_received [::msgcat::mc m57] [::msgcat::mc m58]] $deaths [::DuckHunt::plural $deaths [::msgcat::mc m59] [::msgcat::mc m60]] $deflected_bullets [::DuckHunt::plural $deflected_bullets [::msgcat::mc m61] [::msgcat::mc m62]] $neutralized_bullets [::DuckHunt::plural $neutralized_bullets [::msgcat::mc m63] [::msgcat::mc m64]]]${items_list}$effects_list "
::DuckHunt::purge_db_from_memory
}
return
}
###############################################################################
### Affichage du nombre de munitions dans le chargeur.
###############################################################################
proc ::DuckHunt::display_ammo {lower_nick chan ammos_per_clip} {
if { $::DuckHunt::unlimited_ammo_per_clip } {
# Texte : "\00303Inf.\003"
set displayed_ammo [::msgcat::mc m65]
} else {
set displayed_ammo "[::DuckHunt::colorize_value [::DuckHunt::get_data $lower_nick $chan "current_ammo_clip"]]/$ammos_per_clip"
}
}
###############################################################################
### Affichage du nombre de chargeurs.
###############################################################################
proc ::DuckHunt::display_clips {lower_nick chan ammo_clips} {
if { $::DuckHunt::unlimited_ammo_clips } {
# Texte : "\00303Inf.\003"
set displayed_clips [::msgcat::mc m65]
} else {
set displayed_clips "[::DuckHunt::colorize_value [::DuckHunt::get_data $lower_nick $chan "remaining_ammo_clips"]]/$ammo_clips"
}
}
###############################################################################
### Retourne le karma d'un chasseur.
###############################################################################
proc ::DuckHunt::calculate_karma {wild_shots humans_shot ducks_shot short} {
if { [expr {$wild_shots + $humans_shot + $ducks_shot}] != 0 } {
set karma [::DuckHunt::format_floating_point_value [expr {100.0 * (-(($wild_shots * 1) + ($humans_shot * 3)) + ($ducks_shot * 2)) / (($wild_shots * 1)+($humans_shot * 3)+($ducks_shot * 2))}] 2]
if { !$short } {
if { $karma < 0 } {
# Texte : "\00304mauvais chasseur à %s%%\003"
set karma [::msgcat::mc m39 [expr {abs($karma)}]]
} else {
# Texte : "\00303bon chasseur à %s%%\003"
set karma [::msgcat::mc m40 $karma]
}
}
} else {
if { $short } {
set karma 0
} else {
# Texte : "neutre"
set karma [::msgcat::mc m41]
}
}
return $karma
}
###############################################################################
### !shop [[id] cible] : Affiche une liste des objets qu'il est possible
### d'acheter ou effectue un achat si id est spécifié.
###############################################################################
proc ::DuckHunt::shop {nick host hand chan arg} {
if {
(![channel get $chan DuckHunt])
|| ($hand in $::DuckHunt::blacklisted_handles)
|| (($::DuckHunt::antiflood == 1)
&& (([::DuckHunt::antiflood $nick $chan "nick" $::DuckHunt::shop_cmd $::DuckHunt::flood_shop])
|| ([::DuckHunt::antiflood $nick $chan "chan" "*" $::DuckHunt::flood_global])))
} then {
return
} else {
set lower_nick [::tcl::string::tolower $nick]
::DuckHunt::read_database
::DuckHunt::ckeck_for_pending_rename $chan $nick $lower_nick [md5 "$chan,$lower_nick"]
::DuckHunt::initialize_player $nick $lower_nick $chan
set current_time [unixtime]
# Les joueurs ne peuvent utiliser le shop que si leur arme n'a pas été
# confisquée de façon permanente.
if { [::DuckHunt::get_data $lower_nick $chan "gun"] != -1 } {
if { [::tcl::dict::exists $::DuckHunt::duck_sessions $chan] } {
# On note le cumul du temps de réaction du joueur.
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "cumul_reflex_time" [expr {[::DuckHunt::get_data $lower_nick $chan "cumul_reflex_time"] + [expr {[::tcl::clock::milliseconds] - [lindex [::tcl::dict::get $::DuckHunt::duck_sessions $chan] 0 1]}]}]
}
if { $::DuckHunt::preferred_display_mode == 1 } {
set output_method "PRIVMSG"
set output_target $chan
} else {
set output_method "NOTICE"
set output_target $nick
}
lassign [set args [split [::tcl::string::trim $arg]]] item_id target_nick
set lower_target_nick [::tcl::string::tolower $target_nick]
if {
(($item_id in {1 2 3 4 5 6 7 8 9 10 11 12 13 18 19 20 21 22 23}) && ([llength $args] != 1))
|| (($item_id in {14 15 16 17}) && ([llength $args] != 2))
|| ($item_id ni {{} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23})
} then {
# Message : "\037Syntaxe\037 : \002%s\002 \00314\[\003id \00314\[\003cible\00314\]\]\003 \00307|\003 Affiche une liste des objets qu'il est possible d'acheter ou effectue un achat si \"id\" est spécifié. Certains objet nécessitent qu'une cible soit aussi spécifiée."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m264 $::DuckHunt::shop_cmd]
} elseif { $item_id eq "" } {
if { $::DuckHunt::shop_preferred_display_mode } {
# Message : "\00314\[%s\]\003 Objets disponibles à l'achat : %s \00307|\003 \037Syntaxe\037 : \002%s\002 \00314\[\003id \00314\[\003cible\00314\]\]\003"
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m263 $::DuckHunt::scriptname $::DuckHunt::shop_url $::DuckHunt::shop_cmd]
} else {
# Message : "\00314\[%s\]\003 \037Objets disponibles à l'achat\037 : 1- Balle supp. (%s xp) \00314|\003 2- Chargeur supp. (%s xp) \00314|\003 3- Munitions AP (%s xp) \00314|\003 4- Munitions explosives (%s xp) \00314|\003 5- Rachat arme confisq. (%s xp) \00314|\003 6- Graisse (%s xp) \00314|\003 7- Lunette de visée (%s xp) \00314|\003 8- Détecteur infrarouge (%s xp) \00314|\003 9- Silencieux (%s xp) \00314|\003 10- Trèfle à 4 feuilles (%s xp) \00314|\003 11- Lunettes de soleil (%s xp) \00314|\003 12- Vêtements de rechange (%s xp) \00314|\003 13- Goupillon (%s xp)\00314|\003 14- Miroir (%s xp) \00314|\003 15- Poignée de sable (%s xp) \00314|\003 16- Seau d'eau (%s xp) \00314|\003 17- Sabotage (%s xp) \00314|\003 18- Assurance vie (%s xp) \00314|\003 19- Assurance responsabilité civile (%s xp) \00314|\003 20- Appeau (%s xp) \00314|\003 21- Morceaux de pain (%s xp) \00314|\003 22- Détecteur de canards (%s xp) \00314|\003 23- Canard mécanique (%s xp) \00307|\003 \037Syntaxe\037 : \002%s\002 \00314\[\003id \00314\[\003cible\00314\]\]\003"
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m265 $::DuckHunt::scriptname $::DuckHunt::extra_ammo_cost $::DuckHunt::extra_clip_cost $::DuckHunt::AP_ammo_cost $::DuckHunt::explosive_ammo_cost $::DuckHunt::hand_back_confiscated_weapon_cost $::DuckHunt::grease_cost $::DuckHunt::sight_cost $::DuckHunt::infrared_detector_cost $::DuckHunt::silencer_cost $::DuckHunt::four_leaf_clover_cost $::DuckHunt::sunglasses_cost $::DuckHunt::spare_clothes_cost $::DuckHunt::brush_for_weapon_cost $::DuckHunt::mirror_cost $::DuckHunt::sand_cost $::DuckHunt::water_bucket_cost $::DuckHunt::sabotage_cost $::DuckHunt::life_insurance_cost $::DuckHunt::liability_insurance_cost $::DuckHunt::decoy_cost $::DuckHunt::piece_of_bread_cost $::DuckHunt::duck_detector_cost $::DuckHunt::fake_duck_cost $::DuckHunt::shop_cmd]
}
} else {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "last_activity" $current_time
set prices {1 $::DuckHunt::extra_ammo_cost 2 $::DuckHunt::extra_clip_cost 3 $::DuckHunt::AP_ammo_cost 4 $::DuckHunt::explosive_ammo_cost 5 $::DuckHunt::hand_back_confiscated_weapon_cost 6 $::DuckHunt::grease_cost 7 $::DuckHunt::sight_cost 8 $::DuckHunt::infrared_detector_cost 9 $::DuckHunt::silencer_cost 10 $::DuckHunt::four_leaf_clover_cost 11 $::DuckHunt::sunglasses_cost 12 $::DuckHunt::spare_clothes_cost 13 $::DuckHunt::brush_for_weapon_cost 14 $::DuckHunt::mirror_cost 15 $::DuckHunt::sand_cost 16 $::DuckHunt::water_bucket_cost 17 $::DuckHunt::sabotage_cost 18 $::DuckHunt::life_insurance_cost 19 $::DuckHunt::liability_insurance_cost 20 $::DuckHunt::decoy_cost 21 $::DuckHunt::piece_of_bread_cost 22 $::DuckHunt::duck_detector_cost 23 $::DuckHunt::fake_duck_cost}
if { [::DuckHunt::get_data $lower_nick $chan "xp"] - [subst [::tcl::dict::get $prices $item_id]] < $::DuckHunt::min_xp_for_shopping } {
# Message : "%s > Tu n'es pas assez riche pour effectuer cet achat."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m266 $nick]
} else {
if { $target_nick ne "" } {
::DuckHunt::ckeck_for_pending_rename $chan $target_nick $lower_target_nick [md5 "$chan,$lower_target_nick"]
}
set must_write_db 0
lassign [::DuckHunt::get_level_and_grantings [::DuckHunt::get_data $lower_nick $chan "xp"]] previous_player_lvl {} {} {} {} {} default_ammos_in_clip default_ammo_clips_per_day {} {} {} {}
switch -- $item_id {
1 {
# Balle supplémentaire
if { ![::DuckHunt::get_data $lower_nick $chan "gun"] } {
# Message : "%s > tu n'es pas armé."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m5 $nick]
} elseif { [::DuckHunt::get_data $lower_nick $chan "current_ammo_clip"] >= $default_ammos_in_clip } {
# Message : "%s > Le chargeur de ton arme est déjà plein."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m267 $nick]
} else {
::DuckHunt::incr_data $lower_nick $chan "current_ammo_clip" +1
::DuckHunt::incr_data $lower_nick $chan "xp" "-$::DuckHunt::extra_ammo_cost"
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - - "item_1" 0 -
}
# Message : "%s > Tu viens d'ajouter une balle dans ton arme en échange de %s %s."
# Textes : "point d'xp" "points d'xp"
set output [::msgcat::mc m268 $nick $::DuckHunt::extra_ammo_cost [::DuckHunt::plural $::DuckHunt::extra_ammo_cost [::msgcat::mc m285] [::msgcat::mc m286]]]
set must_write_db 1
}
}
2 {
# Chargeur supplémentaire
if { ![::DuckHunt::get_data $lower_nick $chan "gun"] } {
# Message : "%s > tu n'es pas armé."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m5 $nick]
} elseif { [::DuckHunt::get_data $lower_nick $chan "remaining_ammo_clips"] >= $default_ammo_clips_per_day } {
# Message : "%s > Ta réserve de chargeurs est déjà pleine."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m269 $nick]
} else {
::DuckHunt::incr_data $lower_nick $chan "remaining_ammo_clips" +1
::DuckHunt::incr_data $lower_nick $chan "xp" "-$::DuckHunt::extra_clip_cost"
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - - "item_2" 0 -
}
# Message : "%s > Tu viens d'ajouter un chargeur à ta réserve en échange de %s %s."
set output [::msgcat::mc m270 $nick $::DuckHunt::extra_clip_cost [::DuckHunt::plural $::DuckHunt::extra_clip_cost [::msgcat::mc m285] [::msgcat::mc m286]]]
set must_write_db 1
}
}
3 {
# Munitions AP
lassign [::DuckHunt::get_item_info $lower_nick $chan "3"] item_index expiration_date {}
if { $item_index != -1 } {
# Message : "%s > Tu possèdes déjà ce type de munitions. Le bonus expirera dans %s."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m277 $nick [::DuckHunt::adapt_time_resolution [expr {($expiration_date - $current_time) * 1000}] 0]]
} else {
# Si le joueur possède déjà des munitions explosives, on les remplace.
if { [set item_index [lsearch -index 1 [::DuckHunt::get_data $lower_nick $chan "items"] "4"]] != -1 } {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [lreplace [::DuckHunt::get_data $lower_nick $chan "items"] $item_index $item_index]
}
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [concat [::DuckHunt::get_data $lower_nick $chan "items"] [list [list [expr {$current_time + 86400}] "3" "-"]]]
::DuckHunt::incr_data $lower_nick $chan "xp" "-$::DuckHunt::AP_ammo_cost"
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - - "item_3" 0 -
}
# Message : "%s > Tu viens de changer le type de tes munitions en munitions AP (dégâts x2 pendant 24h) en échange de %s %s."
set output [::msgcat::mc m278 $nick $::DuckHunt::AP_ammo_cost [::DuckHunt::plural $::DuckHunt::AP_ammo_cost [::msgcat::mc m285] [::msgcat::mc m286]]]
set must_write_db 1
}
}
4 {
# Munitions explosives
lassign [::DuckHunt::get_item_info $lower_nick $chan "4"] item_index expiration_date {}
if { $item_index != -1 } {
# Message : "%s > Tu possèdes déjà ce type de munitions. Le bonus expirera dans %s."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m277 $nick [::DuckHunt::adapt_time_resolution [expr {($expiration_date - $current_time) * 1000}] 0]]
} else {
# Si le joueur possède déjà des munitions AP, on les remplace.
if { [set item_index [lsearch -index 1 [::DuckHunt::get_data $lower_nick $chan "items"] "3"]] != -1 } {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [lreplace [::DuckHunt::get_data $lower_nick $chan "items"] $item_index $item_index]
}
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [concat [::DuckHunt::get_data $lower_nick $chan "items"] [list [list [expr {$current_time + 86400}] "4" "-"]]]
::DuckHunt::incr_data $lower_nick $chan "xp" "-$::DuckHunt::explosive_ammo_cost"
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - - "item_4" 0 -
}
# Message : "%s > Tu viens de changer le type de tes munitions en munitions explosives (dégâts x3 pendant 24h) en échange de %s %s."
set output [::msgcat::mc m279 $nick $::DuckHunt::explosive_ammo_cost [::DuckHunt::plural $::DuckHunt::explosive_ammo_cost [::msgcat::mc m285] [::msgcat::mc m286]]]
set must_write_db 1
}
}
5 {
# Rachat d'arme confisquée
if { [::DuckHunt::get_data $lower_nick $chan "gun"] } {
# Message : "%s > Ton arme n'est pas confisquée."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m281 $nick]
} else {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "gun" 1
::DuckHunt::incr_data $lower_nick $chan "xp" "-$::DuckHunt::hand_back_confiscated_weapon_cost"
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - - "item_5" 0 -
}
# Message : "%s > Tu viens de racheter l'arme qui t'avait été confisquée en échange de %s %s."
set output [::msgcat::mc m282 $nick $::DuckHunt::hand_back_confiscated_weapon_cost [::DuckHunt::plural $::DuckHunt::hand_back_confiscated_weapon_cost [::msgcat::mc m285] [::msgcat::mc m286]]]
set must_write_db 1
}
}
6 {
# Graisse
lassign [::DuckHunt::get_item_info $lower_nick $chan "6"] item_index expiration_date {}
if { $item_index != -1 } {
# Message : "%s > Tu possèdes déjà cet item. Il expirera dans %s."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m283 $nick [::DuckHunt::adapt_time_resolution [expr {($expiration_date - $current_time) * 1000}] 0]]
} else {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [concat [::DuckHunt::get_data $lower_nick $chan "items"] [list [list [expr {$current_time + 86400}] "6" "-"]]]
::DuckHunt::incr_data $lower_nick $chan "xp" "-$::DuckHunt::grease_cost"
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - - "item_6" 0 -
}
# Message : "%s > Tu viens de graisser ton arme en échange de %s %s. Le risque d'enrayement est réduit de moitié et la graisse protège une fois contre un jet de sable pendant 24h."
set output [::msgcat::mc m284 $nick $::DuckHunt::grease_cost [::DuckHunt::plural $::DuckHunt::grease_cost [::msgcat::mc m285] [::msgcat::mc m286]]]
set must_write_db 1
}
}
7 {
# Lunette de visée
lassign [::DuckHunt::get_item_info $lower_nick $chan "7"] item_index expiration_date item_uses
if { $item_index != -1 } {
# Message : "%s > Tu possèdes déjà cet item. Il reste %s %s."
# Texte : "utilisation" "utilisations"
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m288 $nick $item_uses [::DuckHunt::plural $item_uses [::msgcat::mc m292] [::msgcat::mc m293]]]
} else {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [concat [::DuckHunt::get_data $lower_nick $chan "items"] [list [list "-" "7" "1"]]]
::DuckHunt::incr_data $lower_nick $chan "xp" "-$::DuckHunt::sight_cost"
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - - "item_7" 0 -
}
# Message : "%s > Tu ajoutes une lunette de visée haute performance à ton arme en échange de %s %s. La précision de ton prochain tir sera augmentée de (100 - précision actuelle) / 3."
set output [::msgcat::mc m287 $nick $::DuckHunt::sight_cost [::DuckHunt::plural $::DuckHunt::sight_cost [::msgcat::mc m285] [::msgcat::mc m286]]]
set must_write_db 1
}
}
8 {
# Détecteur infrarouge
lassign [::DuckHunt::get_item_info $lower_nick $chan "8"] item_index expiration_date item_uses
if { $item_index != -1 } {
# Message : "%s > Tu possèdes déjà cet item. Il expirera dans %s, il reste %s %s."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m295 $nick [::DuckHunt::adapt_time_resolution [expr {($expiration_date - $current_time) * 1000}] 0] $item_uses [::DuckHunt::plural $item_uses [::msgcat::mc m292] [::msgcat::mc m293]]]
} else {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [concat [::DuckHunt::get_data $lower_nick $chan "items"] [list [list [expr {$current_time + 86400}] "8" "6"]]]
::DuckHunt::incr_data $lower_nick $chan "xp" "-$::DuckHunt::infrared_detector_cost"
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - - "item_8" 0 -
}
# Message : "%s > Tu viens d'équiper ton arme d'un détecteur infrarouge en échange de %s %s. Ce dispositif dure 24h et verrouille la gâchette lorsqu'il n'y a pas de canard dans les environs."
set output [::msgcat::mc m289 $nick $::DuckHunt::infrared_detector_cost [::DuckHunt::plural $::DuckHunt::infrared_detector_cost [::msgcat::mc m285] [::msgcat::mc m286]]]
set must_write_db 1
}
}
9 {
# Silencieux
lassign [::DuckHunt::get_item_info $lower_nick $chan "9"] item_index expiration_date {}
if { $item_index != -1 } {
# Message : "%s > Tu possèdes déjà cet item. Il expirera dans %s."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m283 $nick [::DuckHunt::adapt_time_resolution [expr {($expiration_date - $current_time) * 1000}] 0]]
} else {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [concat [::DuckHunt::get_data $lower_nick $chan "items"] [list [list [expr {$current_time + 86400}] "9" "-"]]]
::DuckHunt::incr_data $lower_nick $chan "xp" "-$::DuckHunt::silencer_cost"
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - - "item_9" 0 -
}
# Message : "%s > Tu viens d'équiper ton arme d'un silencieux en échange de %s %s. Grâce à cet équipement, tu n'effraies plus les canards lorsque tu tires pendant 24h."
set output [::msgcat::mc m291 $nick $::DuckHunt::silencer_cost [::DuckHunt::plural $::DuckHunt::silencer_cost [::msgcat::mc m285] [::msgcat::mc m286]]]
set must_write_db 1
}
}
10 {
# Trèfle à 4 feuilles
lassign [::DuckHunt::get_item_info $lower_nick $chan "10"] item_index expiration_date {}
if { $item_index != -1 } {
# Message : "%s > Tu possèdes déjà cet item. Il expirera dans %s."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m283 $nick [::DuckHunt::adapt_time_resolution [expr {($expiration_date - $current_time) * 1000}] 0]]
} else {
set bonus_xp [expr {int(rand()*10) +1}]
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [concat [::DuckHunt::get_data $lower_nick $chan "items"] [list [list [expr {$current_time + 86400}] "10" $bonus_xp]]]
::DuckHunt::incr_data $lower_nick $chan "xp" "-$::DuckHunt::four_leaf_clover_cost"
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - - "item_10" 0 $bonus_xp
}
# Message : "%s > Tu achètes un trèfle à quatre feuilles en échange de %s %s. Ce porte-bonheur te fera gagner %s %s pour chaque canard tué pendant 24h."
set output [::msgcat::mc m294 $nick $::DuckHunt::four_leaf_clover_cost [::DuckHunt::plural $::DuckHunt::four_leaf_clover_cost [::msgcat::mc m285] [::msgcat::mc m286]] $bonus_xp [::DuckHunt::plural $bonus_xp "[::msgcat::mc m285] [::msgcat::mc m424]" "[::msgcat::mc m286] [::msgcat::mc m425]"]]
set must_write_db 1
}
}
11 {
# Lunettes de soleil
lassign [::DuckHunt::get_item_info $lower_nick $chan "11"] item_index expiration_date {}
if { $item_index != -1 } {
# Message : "%s > Tu possèdes déjà cet item. Il expirera dans %s."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m283 $nick [::DuckHunt::adapt_time_resolution [expr {($expiration_date - $current_time) * 1000}] 0]]
} else {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [concat [::DuckHunt::get_data $lower_nick $chan "items"] [list [list [expr {$current_time + 86400}] "11" "-"]]]
::DuckHunt::incr_data $lower_nick $chan "xp" "-$::DuckHunt::sunglasses_cost"
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - - "item_11" 0 -
}
# Message : "%s > Tu achètes une paire de lunettes de soleil en échange de %s %s. Ces lunettes te protègeront contre l'effet éblouissant du miroir pendant 24h."
set output [::msgcat::mc m296 $nick $::DuckHunt::sunglasses_cost [::DuckHunt::plural $::DuckHunt::sunglasses_cost [::msgcat::mc m285] [::msgcat::mc m286]]]
set must_write_db 1
}
}
12 {
# Vêtements de rechange
set item_index [lindex [::DuckHunt::get_item_info $lower_nick $chan "16"] 0]
if { $item_index == -1 } {
# Message : "%s > Tu n'as pas besoin de changer de vêtements."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m297 $nick]
} else {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [lreplace [::DuckHunt::get_data $lower_nick $chan "items"] $item_index $item_index]
::DuckHunt::incr_data $lower_nick $chan "xp" "-$::DuckHunt::spare_clothes_cost"
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - - "item_12" 0 -
}
# Message : "%s > Tu achètes des vêtements secs en échange de %s %s."
set output [::msgcat::mc m298 $nick $::DuckHunt::spare_clothes_cost [::DuckHunt::plural $::DuckHunt::spare_clothes_cost [::msgcat::mc m285] [::msgcat::mc m286]]]
set must_write_db 1
}
}
13 {
# Goupillon
set sand_item_index [lindex [::DuckHunt::get_item_info $lower_nick $chan "15"] 0]
if {
($sand_item_index == -1)
&& ([lindex [::DuckHunt::get_item_info $lower_nick $chan "17"] 0] == -1)
} then {
# Message : "%s > Tu n'as pas besoin d'utiliser un goupillon."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m299 $nick]
} else {
if { $sand_item_index != -1 } {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [lreplace [::DuckHunt::get_data $lower_nick $chan "items"] $sand_item_index $sand_item_index]
}
set sabotage_item_index [lindex [::DuckHunt::get_item_info $lower_nick $chan "17"] 0]
if { $sabotage_item_index != -1 } {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [lreplace [::DuckHunt::get_data $lower_nick $chan "items"] $sabotage_item_index $sabotage_item_index]
}
::DuckHunt::incr_data $lower_nick $chan "xp" "-$::DuckHunt::brush_for_weapon_cost"
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - - "item_13" 0 -
}
# Message : "%s > Tu achètes un goupillon et remets ton arme en état en échange de %s %s."
set output [::msgcat::mc m300 $nick $::DuckHunt::brush_for_weapon_cost [::DuckHunt::plural $::DuckHunt::brush_for_weapon_cost [::msgcat::mc m285] [::msgcat::mc m286]]]
set must_write_db 1
}
}
14 {
# Miroir
if { ![::tcl::dict::exists $::DuckHunt::player_data $chan $lower_target_nick] } {
# Message : "%s > Je ne connais aucun chasseur portant ce nom."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m362 $nick]
} elseif { ![onchan $target_nick $chan] } {
# Message : "%s > Tu ne peux pas éblouir %s puisqu'il n'est pas là."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m363 $nick $target_nick]
} elseif { [lindex [::DuckHunt::get_item_info $lower_target_nick $chan "14"] 0] != -1 } {
# Message : "%s > %s est déjà ébloui."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m324 $nick $target_nick]
} elseif { [lindex [::DuckHunt::get_item_info $lower_nick $chan "11"] 0] != -1 } {
::DuckHunt::incr_data $lower_nick $chan "xp" "-$::DuckHunt::mirror_cost"
# Message : "%s > Ta tentative d'éblouir %s échoue car il porte des lunettes de soleil. Ton échec te coûte quand même %s %s."
set output [::msgcat::mc m325 $nick $target_nick $::DuckHunt::mirror_cost [::DuckHunt::plural $::DuckHunt::mirror_cost [::msgcat::mc m285] [::msgcat::mc m286]]]
set must_write_db 1
} else {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_target_nick "items" [concat [::DuckHunt::get_data $lower_target_nick $chan "items"] [list [list "-" "14" $nick]]]
::DuckHunt::incr_data $lower_nick $chan "xp" "-$::DuckHunt::mirror_cost"
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick $target_nick - "item_14" 0 -
}
# Message : "%s > Tu achètes un miroir en échange de %s %s puis tu t'en sers pour éblouir %s grâce à un rayon de soleil, réduisant ainsi de 50%% la précision de son prochain tir."
set output [::msgcat::mc m326 $nick $::DuckHunt::mirror_cost [::DuckHunt::plural $::DuckHunt::mirror_cost [::msgcat::mc m285] [::msgcat::mc m286]] $target_nick]
set must_write_db 1
}
}
15 {
# Poignée de sable
if { ![::tcl::dict::exists $::DuckHunt::player_data $chan $lower_target_nick] } {
# Message : "%s > Je ne connais aucun chasseur portant ce nom."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m362 $nick]
} elseif { ![onchan $target_nick $chan] } {
# Message : "%s > Tu ne peux pas jeter de sable dans l'arme de %s puisqu'il n'est pas là."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m364 $nick $target_nick]
} elseif { [::DuckHunt::get_data $lower_target_nick $chan "gun"] < 1 } {
# Message : "%s > Tu ne peux pas jeter de sable dans l'arme de %s puisqu'il n'en a pas."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m367 $nick $target_nick]
} elseif { [lindex [::DuckHunt::get_item_info $lower_target_nick $chan "15"] 0] != -1 } {
# Message : "%s > %s a déjà du sable dans son arme."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m327 $nick $target_nick]
} elseif { [set item_index [lindex [::DuckHunt::get_item_info $lower_target_nick $chan "6"] 0]] != -1 } {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_target_nick "items" [lreplace [::DuckHunt::get_data $lower_target_nick $chan "items"] $item_index $item_index]
::DuckHunt::incr_data $lower_nick $chan "xp" "-$::DuckHunt::sand_cost"
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick $target_nick - "item_15" 0 -
}
# Message : "%s > Tu jettes une poignée de sable dans l'arme de %s en échange de %s %s, mais son arme était graissée et le sable n'a eu aucun effet."
set output [::msgcat::mc m328 $nick $target_nick $::DuckHunt::sand_cost [::DuckHunt::plural $::DuckHunt::sand_cost [::msgcat::mc m285] [::msgcat::mc m286]]]
set must_write_db 1
} else {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_target_nick "items" [concat [::DuckHunt::get_data $lower_target_nick $chan "items"] [list [list "-" "15" $nick]]]
::DuckHunt::incr_data $lower_nick $chan "xp" "-$::DuckHunt::sand_cost"
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick $target_nick - "item_15" 0 -
}
# Message : "%s > Tu jettes une poignée de sable dans l'arme de %s en échange de %s %s, réduisant ainsi de 50%% la fiabilité de son arme pour son prochain tir."
set output [::msgcat::mc m329 $nick $target_nick $::DuckHunt::sand_cost [::DuckHunt::plural $::DuckHunt::sand_cost [::msgcat::mc m285] [::msgcat::mc m286]]]
set must_write_db 1
}
}
16 {
# Seau d'eau
if { ![::tcl::dict::exists $::DuckHunt::player_data $chan $lower_target_nick] } {
# Message : "%s > Je ne connais aucun chasseur portant ce nom."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m362 $nick]
} elseif { ![onchan $target_nick $chan] } {
# Message : "%s > Tu ne peux pas jeter de seau d'eau à %s puisqu'il n'est pas là."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m365 $nick $target_nick]
} elseif { [lindex [::DuckHunt::get_item_info $lower_target_nick $chan "16"] 0] != -1 } {
# Message : "%s > %s est déjà trempé."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m330 $nick $target_nick]
} else {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_target_nick "items" [concat [::DuckHunt::get_data $lower_target_nick $chan "items"] [list [list [expr {$current_time + 3600}] "16" $nick]]]
::DuckHunt::incr_data $lower_nick $chan "xp" "-$::DuckHunt::water_bucket_cost"
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick $target_nick - "item_16" 0 -
}
# Message : "%s > Tu jettes un seau d'eau sur %s en échange de %s %s, l'obligeant ainsi à attendre 1h que ses vêtements soient secs avant de pouvoir chasser à nouveau."
set output [::msgcat::mc m331 $nick $target_nick $::DuckHunt::water_bucket_cost [::DuckHunt::plural $::DuckHunt::water_bucket_cost [::msgcat::mc m285] [::msgcat::mc m286]]]
set must_write_db 1
}
}
17 {
# Sabotage
if { ![::tcl::dict::exists $::DuckHunt::player_data $chan $lower_target_nick] } {
# Message : "%s > Je ne connais aucun chasseur portant ce nom."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m362 $nick]
} elseif { ![onchan $target_nick $chan] } {
# Message : "%s > Tu ne peux pas saboter l'arme de %s puisqu'il n'est pas là."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m366 $nick $target_nick]
} elseif { [::DuckHunt::get_data $lower_target_nick $chan "gun"] < 1 } {
# Message : "%s > Tu ne peux pas saboter l'arme de %s puisqu'il n'en a pas."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m368 $nick $target_nick]
} elseif { [lindex [::DuckHunt::get_item_info $lower_target_nick $chan "17"] 0] != -1 } {
# Message : "%s > L'arme de %s a déjà été sabotée."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m335 $nick $target_nick]
} else {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_target_nick "items" [concat [::DuckHunt::get_data $lower_target_nick $chan "items"] [list [list "-" "17" $nick]]]
::DuckHunt::incr_data $lower_nick $chan "xp" "-$::DuckHunt::sabotage_cost"
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick $target_nick - "item_17" 0 -
}
# Message : "%s > Tu enfonces des trucs et des machins dans le canon de l'arme de %s en échange de %s %s."
set output [::msgcat::mc m336 $nick $target_nick $::DuckHunt::sabotage_cost [::DuckHunt::plural $::DuckHunt::sabotage_cost [::msgcat::mc m285] [::msgcat::mc m286]]]
set must_write_db 1
}
}
18 {
# Assurance vie
lassign [::DuckHunt::get_item_info $lower_nick $chan "18"] item_index expiration_date item_uses
if { $item_index != -1 } {
# Message : "%s > Tu possèdes déjà cet item. Il expirera dans %s, il reste %s %s."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m295 $nick [::DuckHunt::adapt_time_resolution [expr {($expiration_date - $current_time) * 1000}] 0] $item_uses [::DuckHunt::plural $item_uses [::msgcat::mc m292] [::msgcat::mc m293]]]
} else {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [concat [::DuckHunt::get_data $lower_nick $chan "items"] [list [list [expr {$current_time + 604800}] "18" "1"]]]
::DuckHunt::incr_data $lower_nick $chan "xp" "-$::DuckHunt::life_insurance_cost"
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - - "item_18" 0 -
}
# Message : "%s > Tu achètes une assurance vie en échange de %s %s. Pendant 1 semaine, si tu es victime d'un accident de chasse, tu gagnes l'équivalent de 2x le niveau du tireur en points d'xp. Cette assurance est à usage unique."
set output [::msgcat::mc m339 $nick $::DuckHunt::life_insurance_cost [::DuckHunt::plural $::DuckHunt::life_insurance_cost [::msgcat::mc m285] [::msgcat::mc m286]]]
set must_write_db 1
}
}
19 {
# Assurance responsabilité civile
lassign [::DuckHunt::get_item_info $lower_nick $chan "19"] item_index expiration_date {}
if { $item_index != -1 } {
# Message : "%s > Tu possèdes déjà cet item. Il expirera dans %s."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m283 $nick [::DuckHunt::adapt_time_resolution [expr {($expiration_date - $current_time) * 1000}] 0]]
} else {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [concat [::DuckHunt::get_data $lower_nick $chan "items"] [list [list [expr {$current_time + 172800}] "19" "-"]]]
::DuckHunt::incr_data $lower_nick $chan "xp" "-$::DuckHunt::liability_insurance_cost"
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - - "item_19" 0 -
}
# Message : "%s > Tu achètes une assurance responsabilité civile en échange de %s %s. Pendant 2 jours, la pénalité d'xp sera divisée par 3 si tu provoques un accident de chasse."
set output [::msgcat::mc m342 $nick $::DuckHunt::liability_insurance_cost [::DuckHunt::plural $::DuckHunt::liability_insurance_cost [::msgcat::mc m285] [::msgcat::mc m286]]]
set must_write_db 1
}
}
20 {
# Appeau
if {
([strftime "%H" $current_time] in $::DuckHunt::duck_sleep_hours)
&& ($::DuckHunt::cant_attract_ducks_when_sleeping)
} then {
# Message : "%s > À cette heure-ci, les canards dorment."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m388 $nick]
} else {
if { $::DuckHunt::decoys_can_attract_golden_ducks } {
utimer [expr {int(rand()*600) +1}] [list ::DuckHunt::duck_soaring $chan - 0 - - - - - -]
} else {
utimer [expr {int(rand()*600) +1}] [list ::DuckHunt::duck_soaring $chan 0 0 - - - - - -]
}
::DuckHunt::incr_data $lower_nick $chan "xp" "-$::DuckHunt::decoy_cost"
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - - "item_20" 0 -
}
# Message : "%s > Tu achètes et utilises un appeau en échange de %s %s, ce qui devrait attirer un canard dans les 10 prochaines minutes."
set output [::msgcat::mc m344 $nick $::DuckHunt::decoy_cost [::DuckHunt::plural $::DuckHunt::decoy_cost [::msgcat::mc m285] [::msgcat::mc m286]]]
set must_write_db 1
}
}
21 {
# Morceau de pain
if {
([strftime "%H" $current_time] in $::DuckHunt::duck_sleep_hours)
&& ($::DuckHunt::cant_attract_ducks_when_sleeping)
} then {
# Message : "%s > À cette heure-ci, les canards dorment."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m388 $nick]
} elseif { [llength [channel get $chan DuckHunt-PiecesOfBread]] == $::DuckHunt::max_bread_on_chan } {
# Message : "%s > Il y a déjà %s morceaux de pain sur %s, ça devrait suffire pour l'instant."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m387 $nick $::DuckHunt::max_bread_on_chan $chan]
} else {
channel set $chan DuckHunt-PiecesOfBread [concat [channel get $chan DuckHunt-PiecesOfBread] [expr {$current_time + 3600}]]
if { $::DuckHunt::method == 2 } {
::DuckHunt::replan_flights - - - $chan $nick
}
::DuckHunt::incr_data $lower_nick $chan "xp" "-$::DuckHunt::piece_of_bread_cost"
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - - "item_21" 0 -
}
# Message : "%s > Tu achètes un morceau de pain en échange de %s %s, augmentant ainsi les chances d'attirer des canards pendant 1h et retardant leur départ. Il y a actuellement %s %s sur %s."
# Textes : "morceau de pain" "morceaux de pain"
set output [::msgcat::mc m347 $nick $::DuckHunt::piece_of_bread_cost [::DuckHunt::plural $::DuckHunt::piece_of_bread_cost [::msgcat::mc m285] [::msgcat::mc m286]] [set num_pieces_of_bread [llength [channel get $chan DuckHunt-PiecesOfBread]]] [::DuckHunt::plural $num_pieces_of_bread [::msgcat::mc m348] [::msgcat::mc m349]] $chan]
set must_write_db 1
}
}
22 {
# Détecteur de canards
lassign [::DuckHunt::get_item_info $lower_nick $chan "22"] item_index expiration_date item_uses
if { $item_index != -1 } {
# Message : "%s > Tu possèdes déjà cet item. Il reste %s %s."
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m288 $nick $item_uses [::DuckHunt::plural $item_uses [::msgcat::mc m292] [::msgcat::mc m293]]]
} else {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [concat [::DuckHunt::get_data $lower_nick $chan "items"] [list [list "-" "22" "1"]]]
::DuckHunt::incr_data $lower_nick $chan "xp" "-$::DuckHunt::duck_detector_cost"
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - - "item_22" 0 -
}
# Message : "%s > Tu achètes un détecteur de canards en échange de %s %s. Tu seras averti par une notice lorsque le prochain canard s'envolera."
set output [::msgcat::mc m350 $nick $::DuckHunt::duck_detector_cost [::DuckHunt::plural $::DuckHunt::duck_detector_cost [::msgcat::mc m285] [::msgcat::mc m286]]]
set must_write_db 1
}
}
23 {
# Canard mécanique
utimer 600 [list ::DuckHunt::duck_soaring $chan 0 1 $nick - - - - -]
::DuckHunt::incr_data $lower_nick $chan "xp" "-$::DuckHunt::fake_duck_cost"
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan $current_time $nick $lower_nick - - "item_23" 0 -
}
# Message : "%s > Tu achètes un canard mécanique en échange de %s %s, puis tu le programmes pour décoller dans exactement 10mn."
set output [::msgcat::mc m361 $nick $::DuckHunt::fake_duck_cost [::DuckHunt::plural $::DuckHunt::fake_duck_cost [::msgcat::mc m285] [::msgcat::mc m286]]]
set must_write_db 1
}
}
if { $must_write_db } {
::DuckHunt::recalculate_ammo_on_lvl_change $lower_nick $chan
::DuckHunt::write_database
}
if { $previous_player_lvl > [set current_player_lvl [lindex [::DuckHunt::get_level_and_grantings [::DuckHunt::get_data $lower_nick $chan "xp"]] 0]] } {
# Message : "%s > Ton achat t'a fait repasser au niveau %s (%s)."
append output [::msgcat::mc m280 $current_player_lvl [::DuckHunt::lvl2rank $current_player_lvl]]
}
if { [::tcl::info::exists output] } {
::DuckHunt::display_output help $output_method $output_target $output
}
}
}
}
::DuckHunt::purge_db_from_memory
}
return
}
###############################################################################
### !unarm [-static] <nick> : Désarme un chasseur.
### Un joueur ainsi désarmé ne sera jamais réarmé automatiquement.
### Il faudra utiliser la commande !rearm pour le réarmer manuellement.
###############################################################################
proc ::DuckHunt::unarm {nick host hand chan arg} {
if { [channel get $chan DuckHunt] } {
if { [set arg [split [::tcl::string::trim $arg]]] == {} } {
# Message : "\037Syntaxe\037 : \002%s\002 \00314\[\003-static\00314\] <\003nick\00314>\003 \00307|\003 Désarme un joueur. Le paramètre -static permet de s'assurer qu'il ne sera pas réarmé automatiquement; seule la commande \"%s\" le permettra. En l'absence de ce paramètre, l'arme sera automatiquement rendue au joueur lors de la prochaine dé-confiscation automatique."
::DuckHunt::display_output help PRIVMSG $chan [::msgcat::mc m66 $::DuckHunt::unarm_cmd]
} else {
if { [::tcl::string::tolower [set target [lindex $arg 0]]] eq "-static" } {
set is_static 1
set target [lindex $arg 1]
} else {
set is_static 0
}
set lower_target [::tcl::string::tolower $target]
::DuckHunt::read_database
::DuckHunt::ckeck_for_pending_rename $chan $nick $lower_target [md5 "$chan,$lower_target"]
if {
!([::tcl::dict::exists $::DuckHunt::player_data $chan])
|| !([::tcl::dict::exists $::DuckHunt::player_data $chan $lower_target])
} then {
# Message : "%s > %s n'a pas été trouvé dans la liste des chasseurs de canards sur %s."
::DuckHunt::display_output help PRIVMSG $chan [::msgcat::mc m67 $nick $target $chan]
} else {
# Désarmement permanent.
if { $is_static } {
# Si $target est déjà désarmé de façon permanente.
if { [::DuckHunt::get_data $lower_target $chan "gun"] == -1 } {
# Message : "%s fouille %s et ne trouve aucune arme sur lui. Il se rappelle maintenant que % a déjà été désarmé de façon permanente."
::DuckHunt::display_output help PRIVMSG $chan [::msgcat::mc m68 $nick $target $target]
# Si $target était auparavant armé.
} elseif { [::DuckHunt::get_data $lower_target $chan "gun"] == 1 } {
::DuckHunt::incr_data $lower_target $chan "confiscated_weapons" +1
::tcl::dict::set ::DuckHunt::player_data $chan $lower_target "gun" -1
# Message : "%s fouille %s et le désarme."
::DuckHunt::display_output help PRIVMSG $chan [::msgcat::mc m69 $nick $target]
::DuckHunt::write_database
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan [unixtime] $nick - $target - "perm_unarm" 0 -
}
# Si $target était déjà temporairement désarmé.
} else {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_target "gun" -1
# Message : "%s fouille %s et ne trouve aucune arme sur lui; il fait cependant le nécessaire pour que son arme ne lui soit pas redonnée lors de la prochaine restitution automatique."
::DuckHunt::display_output help PRIVMSG $chan [::msgcat::mc m70 $nick $target]
::DuckHunt::write_database
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan [unixtime] $nick - $target - "perm_unarm" 0 -
}
}
# Désarmement temporaire.
} else {
# Si $target était auparavant désarmé de façon permanente.
if { [::DuckHunt::get_data $lower_target $chan "gun"] == -1 } {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_target "gun" 0
# Message : "%s ne rend pas immédiatement son arme à %s, mais fait le nécessaire pour qu'elle lui soit rendue lors de la prochaine restitution automatique des armes."
::DuckHunt::display_output help PRIVMSG $chan [::msgcat::mc m128 $nick $target]
::DuckHunt::write_database
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan [unixtime] $nick - $target - "temp_unarm" 0 -
}
# Si $target était auparavant armé.
} elseif { [::DuckHunt::get_data $lower_target $chan "gun"] == 1 } {
::DuckHunt::incr_data $lower_target $chan "confiscated_weapons" +1
::tcl::dict::set ::DuckHunt::player_data $chan $lower_target "gun" 0
# Message : "%s fouille %s et lui confisque son arme. Elle lui sera rendue lors de la prochaine restitution automatique des armes."
::DuckHunt::display_output help PRIVMSG $chan [::msgcat::mc m129 $nick $target]
::DuckHunt::write_database
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan [unixtime] $nick - $target - "temp_unarm" 0 -
}
# Si $target est déjà temporairement désarmé.
} else {
# Message : "%s fouille %s et ne trouve aucune arme sur lui."
::DuckHunt::display_output help PRIVMSG $chan [::msgcat::mc m130 $nick $target]
}
}
}
::DuckHunt::purge_db_from_memory
}
}
}
###############################################################################
### !rearm <nick> : Redonne son arme à un chasseur.
###############################################################################
proc ::DuckHunt::rearm {nick host hand chan target} {
if { [channel get $chan DuckHunt] } {
if { [set target [::tcl::string::trim $target]] == "" } {
# Message : "\037Syntaxe\037 : \002%s\002 \00314<\003nick\00314>\003 \00307|\003 Rend son arme à un joueur qui a été désarmé automatiquement ou manuellement au moyen de la commande \"%s\"."
::DuckHunt::display_output help PRIVMSG $chan [::msgcat::mc m71 $::DuckHunt::rearm_cmd]
} else {
set lower_target [::tcl::string::tolower $target]
::DuckHunt::read_database
::DuckHunt::ckeck_for_pending_rename $chan $nick $lower_target [md5 "$chan,$lower_target"]
if {
!([::tcl::dict::exists $::DuckHunt::player_data $chan])
|| !([::tcl::dict::exists $::DuckHunt::player_data $chan $lower_target])
} then {
# Message : "%s > %s n'a pas été trouvé dans la liste des chasseurs de canards sur %s."
::DuckHunt::display_output help PRIVMSG $chan [::msgcat::mc m67 $nick $target $chan]
} else {
if { [::DuckHunt::get_data $lower_target $chan "gun"] == 1 } {
# Message : "%s a déjà une arme et regarde %s sans comprendre."
::DuckHunt::display_output help PRIVMSG $chan [::msgcat::mc m72 $target $nick]
} else {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_target "gun" 1
# Message : "%s rend son arme à %s."
::DuckHunt::display_output help PRIVMSG $chan [::msgcat::mc m73 $nick $target]
::DuckHunt::write_database
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan [unixtime] $nick - $target - "rearm" 0 -
}
}
}
::DuckHunt::purge_db_from_memory
}
}
}
###############################################################################
### ducklist <chan> [argument de recherche] : Affiche la liste des profils
### utilisateur sur le chan spécifié ou effectue une recherche dans celle-ci.
###############################################################################
proc ::DuckHunt::findplayer {nick host hand arg} {
if { [matchattr $hand $::DuckHunt::findplayer_auth [lindex [split $arg] 0]] } {
if {
([set arg [::tcl::string::trim $arg]] eq "")
|| ([llength [set arg [split $arg]]] < 1)
} then {
# Message : "\037Syntaxe\037 : \002%s\002 \00314<\003chan\00314> \[\003argument de recherche\00314\]\003 \00307|\003 Affiche la liste des profils utilisateur sur le chan spécifié ou effectue une recherche dans celle-ci."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m122 $::DuckHunt::findplayer_cmd]
} else {
lassign $arg chan search_argument
set chan [::DuckHunt::fix_chan_case $chan]
::DuckHunt::read_database
if { ![validchan $chan] } {
# Message : "\00304:::\003 Erreur : %s n'est pas un chan valide."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m75 $chan]
} elseif { ![::tcl::dict::exists $::DuckHunt::player_data $chan] } {
# Message : "\00304:::\003 Erreur : Aucun chasseur n'a été aperçu sur %s."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m123 $chan]
} else {
if { $search_argument eq "" } {
set hunters_list [lsort [::tcl::dict::keys [::tcl::dict::get $::DuckHunt::player_data $chan]]]
} else {
set hunters_list [lsort [lsearch -all -glob -nocase -inline [::tcl::dict::keys [::tcl::dict::get $::DuckHunt::player_data $chan]] "*${search_argument}*"]]
}
if { $hunters_list ne "" } {
set num_hunters [llength $hunters_list]
# Message : "\0371 résultat\037 : %s"
# Message : "\037%s résultats\037 : %s"
::DuckHunt::display_output help NOTICE $nick [::DuckHunt::plural $num_hunters [::msgcat::mc m124 [join $hunters_list]] [::msgcat::mc m125 $num_hunters [join $hunters_list]]]
} else {
# Message : "La recherche de \"%s\" n'a donné aucun résultat."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m126 $search_argument]
}
}
::DuckHunt::purge_db_from_memory
}
}
}
###############################################################################
### duckfusion <chan> <dest_nick> <src_nick1> [[src_nick2] [...]] : Fusionne les
### statistiques de plusieurs profils utilisateur.
###############################################################################
proc ::DuckHunt::fusion {nick host hand arg} {
if { [matchattr $hand $::DuckHunt::fusion_auth [lindex [split $arg] 0]] } {
if {
([set arg [::tcl::string::trim $arg]] eq "")
|| ([llength [set arg [split $arg]]] < 3)
} then {
# Message : "\037Syntaxe\037 : \002%s\002 \00314<\003chan\00314> <\003nick destination\00314> <\003nick source 1\00314> \[\003nick source 2\00314\] \[\003...\00314\]\003 \00307|\003 Fusionne les statistiques de plusieurs profils utilisateur. Les statistiques de tous les nicks source seront fusionnées dans le nick de destination."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m74 $::DuckHunt::fusion_cmd]
} else {
lassign $arg chan dst_nick
set chan [::DuckHunt::fix_chan_case $chan]
set lower_dst_nick [::tcl::string::tolower $dst_nick]
set src_nicks [lrange $arg 2 end]
set must_write_db 0
::DuckHunt::read_database
if { ![validchan $chan] } {
# Message : "\00304:::\003 Erreur : %s n'est pas un chan valide."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m75 $chan]
} elseif { ![channel get $chan DuckHunt] } {
# Message : "\00304:::\003 Erreur : %s n'est pas activé sur %s."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m76 $::DuckHunt::scriptname $chan]
} elseif {
!([::tcl::dict::exists $::DuckHunt::player_data $chan])
|| !([::tcl::dict::exists $::DuckHunt::player_data $chan $lower_dst_nick])
} then {
# Message : "\00304:::\003 Erreur : %s n'a pas été trouvé dans la liste des chasseurs de canards sur %s."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m77 $dst_nick $chan]
} else {
foreach src_nick $src_nicks {
set lower_src_nick [::tcl::string::tolower $src_nick]
if { ![::tcl::dict::exists $::DuckHunt::player_data $chan $lower_src_nick] } {
# Message : "\00304:::\003 Erreur : %s n'a pas été trouvé dans la liste des chasseurs de canards sur %s."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m77 $src_nick $chan]
} else {
set src_stats ""
set dst_stats ""
foreach varname {gun jammed current_ammo_clip remaining_ammo_clips xp ducks_shot missed_shots empty_shots humans_shot wild_shots bullets_received deflected_bullets deaths confiscated_weapons jammed_weapons best_time cumul_reflex_time nick items golden_ducks_shot last_activity} {
lappend src_stats [::DuckHunt::get_data $lower_src_nick $chan $varname]
lappend dst_stats [::DuckHunt::get_data $lower_dst_nick $chan $varname]
}
::DuckHunt::merge_stats $chan $src_nick $lower_src_nick $dst_nick $lower_dst_nick $src_stats $dst_stats 1
set must_write_db 1
# Message : "Les statistiques de chasse de %s et de %s ont été fusionnées dans %s sur le chan %s."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m78 $dst_nick $src_nick $dst_nick $chan]
}
}
if { $must_write_db } {
::DuckHunt::recalculate_ammo_on_lvl_change $lower_dst_nick $chan
::DuckHunt::write_database
}
}
::DuckHunt::purge_db_from_memory
}
}
}
###############################################################################
### duckrename <chan> <source_nick> <destination_nick> : Renomme le profil de
### statistiques d'un utilisateur.
###############################################################################
proc ::DuckHunt::rename_player {nick host hand arg} {
if { [matchattr $hand $::DuckHunt::fusion_auth [lindex [split $arg] 0]] } {
if {
([set arg [::tcl::string::trim $arg]] eq "")
|| ([llength [set arg [split $arg]]] != 3)
} then {
# Message : "\037Syntaxe\037 : \002%s\002 \00314<\003chan\00314> <\003ancien nick\00314> <\003nouveau nick\00314>\003 \00307|\003 Renomme le profil de statistiques d'un utilisateur."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m131 $::DuckHunt::rename_cmd]
} else {
lassign $arg chan old_nick new_nick
set chan [::DuckHunt::fix_chan_case $chan]
set lower_old_nick [::tcl::string::tolower $old_nick]
set lower_new_nick [::tcl::string::tolower $new_nick]
set must_write_db 0
::DuckHunt::read_database
if { ![validchan $chan] } {
# Message : "\00304:::\003 Erreur : %s n'est pas un chan valide."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m75 $chan]
} elseif { ![channel get $chan DuckHunt] } {
# Message : "\00304:::\003 Erreur : %s n'est pas activé sur %s."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m76 $::DuckHunt::scriptname $chan]
} elseif {
!([::tcl::dict::exists $::DuckHunt::player_data $chan])
|| !([::tcl::dict::exists $::DuckHunt::player_data $chan $lower_old_nick])
} then {
# Message : "\00304:::\003 Erreur : %s n'a pas été trouvé dans la liste des chasseurs de canards sur %s."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m77 $old_nick $chan]
} elseif { [::tcl::dict::exists $::DuckHunt::player_data $chan $lower_new_nick] } {
# Message : "\00304:::\003 Erreur : Il existe déjà un profil de statistiques au nom de %s sur %s. Si vous souhaitez fusionner les deux, utilisez plutôt la commande \"%s\"."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m132 $new_nick $chan $::DuckHunt::fusion_cmd]
} else {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_new_nick [::tcl::dict::get $::DuckHunt::player_data $chan $lower_old_nick]
::tcl::dict::set ::DuckHunt::player_data $chan $lower_new_nick "nick" $new_nick
::tcl::dict::unset ::DuckHunt::player_data $chan $lower_old_nick
set must_write_db 1
# Message : "Le profil de statistiques de %s a été renommé en %s sur %s."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m133 $old_nick $new_nick $chan]
if { $must_write_db } {
::DuckHunt::write_database
}
}
::DuckHunt::purge_db_from_memory
}
}
}
###############################################################################
### duckdelete <chan> <nick> : Supprime le profil de statistiques d'un
### utilisateur.
###############################################################################
proc ::DuckHunt::delete_player {nick host hand arg} {
if { [matchattr $hand $::DuckHunt::fusion_auth [lindex [split $arg] 0]] } {
if {
([set arg [::tcl::string::trim $arg]] eq "")
|| ([llength [set arg [split $arg]]] != 2)
} then {
# Message : "\037Syntaxe\037 : \002%s\002 \00314<\003chan\00314> <\003nick\00314>\003 \00307|\003 Supprime le profil de statistiques d'un utilisateur."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m141 $::DuckHunt::delete_cmd]
} else {
lassign $arg chan target
set chan [::DuckHunt::fix_chan_case $chan]
set lower_target [::tcl::string::tolower $target]
::DuckHunt::read_database
if { ![validchan $chan] } {
# Message : "\00304:::\003 Erreur : %s n'est pas un chan valide."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m75 $chan]
} elseif {
!([::tcl::dict::exists $::DuckHunt::player_data $chan])
|| !([::tcl::dict::exists $::DuckHunt::player_data $chan $lower_target])
} then {
# Message : "\00304:::\003 Erreur : il n'existe pas de profil au nom de %s sur %s."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m142 $target $chan]
} else {
::tcl::dict::unset ::DuckHunt::player_data $chan $lower_target
# Message : "Le profil de statistiques de %s a été supprimé sur %s."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m143 $target $chan]
::DuckHunt::write_database
}
::DuckHunt::purge_db_from_memory
}
}
}
###############################################################################
### duckplanning <chan> : Affiche la planification des envols de canards pour la
### journée en cours sur le chan spécifié (si method = 2 uniquement).
###############################################################################
proc ::DuckHunt::show_planning {nick host hand chan} {
if { [matchattr $hand $::DuckHunt::planning_auth [set chan [::DuckHunt::fix_chan_case [::tcl::string::trim $chan]]]] } {
if {
($chan eq "")
|| ([llength [split $chan]] > 1)
} then {
# Message : "\037Syntaxe\037 : \002%s\002 \00314<\003chan\00314>\003 \00307|\003 Affiche la planification des envols de canards pour la journée en cours sur le chan spécifié."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m79 $::DuckHunt::planning_cmd]
} elseif { ![validchan $chan] } {
# Message : "\00304:::\003 Erreur : %s n'est pas un chan valide."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m75 $chan]
} elseif { ![channel get $chan DuckHunt] } {
# Message : "\00304:::\003 Erreur : %s n'est pas activé sur %s."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m76 $::DuckHunt::scriptname $chan]
} elseif { !$::DuckHunt::post_init_done } {
# Message : "\00304:::\003 Erreur : %s est en cours d'initialisation, la planification des envols n'a pas encore été calculée. Veuillez réessayer d'ici quelques instants."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m243 $::DuckHunt::scriptname]
} else {
if { [::tcl::dict::exists $::DuckHunt::planned_soarings $chan] } {
# Message : "Planification des envols de canards sur %s pour la journée en cours : %s"
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m81 $chan [join [lsort [::tcl::dict::get $::DuckHunt::planned_soarings $chan]] ", "]]
} else {
# Message : "Aucun envol de canard n'est planifié pour la journée en cours sur %s."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m154 $chan]
}
}
}
}
###############################################################################
### duckreplanning <chan> : Recalcule une planification différente pour les
### envols de canards pour la journée en cours sur le chan spécifié
### (si method = 2 uniquement).
###############################################################################
proc ::DuckHunt::replan_flights {nick host hand chan args} {
if {
($args ne "")
|| ([matchattr $hand $::DuckHunt::planning_auth [set chan [::DuckHunt::fix_chan_case [::tcl::string::trim $chan]]]])
} then {
if {
($chan eq "")
|| ([llength [split $chan]] > 1)
} then {
# Message : "\037Syntaxe\037 : \002%s\002 \00314<\003chan\00314>\003 \00307|\003 Recalcule une planification différente pour la journée en cours sur le chan spécifié."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m148 $::DuckHunt::replanning_cmd]
} elseif { ![validchan $chan] } {
# Message : "\00304:::\003 Erreur : %s n'est pas un chan valide."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m75 $chan]
} elseif { ![channel get $chan DuckHunt] } {
# Message : "\00304:::\003 Erreur : %s n'est pas activé sur %s."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m76 $::DuckHunt::scriptname $chan]
} elseif { !$::DuckHunt::post_init_done } {
if { $args eq "" } {
# Message : "\00304:::\003 Erreur : %s est en cours d'initialisation, la planification des envols n'a pas encore été calculée. Veuillez réessayer d'ici quelques instants."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m243 $::DuckHunt::scriptname]
}
} else {
if { $args eq "" } {
::DuckHunt::plan_out_flights $chan
# Message : "Une nouvelle planification des envols a été calculée pour le chan %s : %s"
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m149 $chan [join [lsort [::tcl::dict::get $::DuckHunt::planned_soarings $chan]] ", "]]
} elseif { $args eq "@" } {
::DuckHunt::plan_out_flights $chan "bread_expired"
if { $::DuckHunt::show_bread_replanning } {
# Message : "\00314\[%s\]\017 Morceau de pain expiré. Une nouvelle planification des envols a été calculée pour le chan %s : %s"
::DuckHunt::display_output loglev - - [::msgcat::mc m346 $::DuckHunt::scriptname $chan [join [lsort [::tcl::dict::get $::DuckHunt::planned_soarings $chan]] ", "]]
}
} else {
::DuckHunt::plan_out_flights $chan "bread_added"
if { $::DuckHunt::show_bread_replanning } {
# Message : "\00314\[%s\]\017 Morceau de pain acheté par %s. Une nouvelle planification des envols a été calculée pour le chan %s : %s"
::DuckHunt::display_output loglev - - [::msgcat::mc m345 $::DuckHunt::scriptname [join $args] $chan [join [lsort [::tcl::dict::get $::DuckHunt::planned_soarings $chan]] ", "]]
}
}
}
}
}
###############################################################################
### ducklaunch <chan> [golden_duck] : Fait s'envoler un canard.
### golden_duck peut valoir 0 ou 1 et vaudra 0 s'il n'est pas spécifié.
###############################################################################
proc ::DuckHunt::launch {nick host hand arg} {
lassign [set args [split [::tcl::string::trim $arg]]] chan is_golden_duck
if { [matchattr $hand $::DuckHunt::launch_auth $chan] } {
if {
($chan eq "")
|| ([llength $args] > 2)
|| ($is_golden_duck ni {{} 0 1})
} then {
# Message : "\037Syntaxe\037 : \002%s\002 \00314<\003chan\00314> \[\003golden_duck\00314\]\003 \00307|\003 Déclenche l'envol d'un canard sur le chan spécifié. golden_duck détermine s'il s'agit d'un super-canard ou d'un canard normal et peut valoir 0 (normal) ou 1 (super-canard). Si golden_duck est omis, il vaudra 0 par défaut."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m82 $::DuckHunt::launch_cmd]
} elseif { ![validchan $chan] } {
# Message : "\00304:::\003 Erreur : %s n'est pas un chan valide."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m75 $chan]
} elseif { ![channel get $chan DuckHunt] } {
# Message : "\00304:::\003 Erreur : %s n'est pas activé sur %s."
::DuckHunt::display_output help NOTICE $nick [::msgcat::mc m76 $::DuckHunt::scriptname $chan]
} else {
set chan [::DuckHunt::fix_chan_case $chan]
if { $is_golden_duck eq "" } {
set is_golden_duck 0
}
if { $::DuckHunt::hunting_logs } {
if { $is_golden_duck } {
::DuckHunt::add_to_log $chan [unixtime] $nick - - - "golden_duck_launch" 0 -
} else {
::DuckHunt::add_to_log $chan [unixtime] $nick - - - "launch" 0 -
}
}
::DuckHunt::duck_soaring $chan $is_golden_duck 0 -
}
}
}
###############################################################################
### duckexport <chan> [critère de tri] : Exporte les données des joueurs sous
### forme de tableau dans un fichier texte.
### Le critère de tri peut valoir nick xp level xp_lvl_up gun ammo max_ammo
### ammo_clips max_clips accuracy effective_accuracy deflection defense jamming
### jammed jammed_nbr confisc ducks golden_ducks missed empty accidents wild_shots total_ammo
### shot_at neutralized deflected deaths best_time average_reflex_time karma
### rank ou items.
### Si aucun critère de tri n'est spécifié, le tableau sera trié par nick.
###############################################################################
proc ::DuckHunt::export_players_table {src_nick host hand sort_by} {
if { ![matchattr $hand $::DuckHunt::export_auth [set chan [lindex [split $sort_by] 0]]] } {
return
} elseif { [llength [split [::tcl::string::trim $sort_by]]] > 1 } {
# Message : "\037Syntaxe\037 : \002%s\002 \00314\[\003critère de tri\00314\]\003 \00307|\003 Exporte un tableau contenant les données des joueurs dans un fichier texte. Si vous ne spécifiez pas de critère de tri, le tableau sera trié par nick. Le critère de tri peut valoir nick last_activity xp level xp_lvl_up gun ammo max_ammo ammo_clips max_clips accuracy effective_accuracy deflection defense jamming jammed jammed_nbr confisc ducks golden_ducks missed empty accidents wild_shots total_ammo shot_at neutralized deflected deaths best_time average_reflex_time karma rank ou items."
::DuckHunt::display_output help NOTICE $src_nick [::msgcat::mc m205 $::DuckHunt::export_cmd]
} elseif { $sort_by ni [set valid_arguments {{} nick last_activity xp level xp_lvl_up gun ammo max_ammo ammo_clips max_clips accuracy effective_accuracy deflection defense jamming jammed jammed_nbr confisc ducks golden_ducks missed empty accidents wild_shots total_ammo shot_at neutralized deflected deaths best_time average_reflex_time karma rank items}] } {
# Message : "\00304:::\003 Erreur : \"%s\" n'est pas un critère de tri valide. Le critère de tri doit valoir %s"
::DuckHunt::display_output help NOTICE $src_nick [::msgcat::mc m206 $sort_by [lreplace [linsert $valid_arguments end-1 [::msgcat::mc m80]] 0 0]]
} else {
::DuckHunt::read_database
if { $sort_by eq "" } {
set sort_by "nick"
} else {
set sort_by [::tcl::string::tolower $sort_by]
}
set indexes {"nick" 0 "last_activity" 1 "xp" 2 "level" 3 "xp_lvl_up" 4 "ammo" 5 "max_ammo" 6 "ammo_clips" 7 "max_clips" 8 "accuracy" 9 "effective_accuracy" 10 "deflection" 11 "armor" 12 "jamming" 13 "jammed" 14 "jammed_nbr" 15 "gun" 16 "confisc" 17 "ducks" 18 "golden_ducks" 19 "missed" 20 "empty" 21 "accidents" 22 "wild_shots" 23 "total_ammo" 24 "shot_at" 25 "neutralized" 26 "deflected" 27 "deaths" 28 "best_time" 29 "average_reflex_time" 30 "karma" 31 "rank" 32 "items" 33}
set format_string {%-*s%-*s%-*s%-*s%-*s%-*s%-*s%-*s%-*s%-*s%-*s%-*s%-*s%-*s%-*s%-*s%-*s%-*s%-*s%-*s%-*s%-*s%-*s%-*s%-*s%-*s%-*s%-*s%-*s%-*s%-*s%-*s%-*s%-*s}
# Message : "%s v%s (©2015-2016 Menz Agitat) - %s - Rapport généré le %s/%s/%s à %s - trié par %s"
set current_time [unixtime]
set title [::msgcat::mc m207 $::DuckHunt::scriptname $::DuckHunt::version $::network [strftime "%d" $current_time] [strftime "%m" $current_time] [strftime "%Y" $current_time] [strftime "%H:%M:%S" $current_time] $sort_by]
set players_table_file_ID [open $::DuckHunt::players_table_file w]
puts $players_table_file_ID " [::tcl::string::repeat "-" [expr {[::tcl::string::length $title] + 4}]] "
puts $players_table_file_ID "| $title |"
puts $players_table_file_ID " [::tcl::string::repeat "-" [expr {[::tcl::string::length $title] + 4}]] "
puts $players_table_file_ID ""
# Texte : "Critères de tri disponibles :"
puts $players_table_file_ID "[::msgcat::mc m244] nick last_activity xp level xp_lvl_up gun ammo max_ammo ammo_clips max_clips accuracy effective_accuracy deflection defense jamming jammed jammed_nbr confisc ducks golden_ducks missed empty accidents wild_shots total_ammo shot_at neutralized deflected deaths best_time average_reflex_time karma rank items"
puts $players_table_file_ID ""
puts $players_table_file_ID ""
# La base de données est vide.
if { ![llength [set chans_to_process [::tcl::dict::keys $::DuckHunt::player_data]]] } {
# Message : "Aucune donnée"
puts $players_table_file_ID [::msgcat::mc m208]
# La base de données n'est pas vide.
} else {
# On calcule la largeur des colonnes.
set rank_name_max_length 0
foreach {lvl rank_name} [::msgcat::mc m134] {
if { [set length [::tcl::string::length $rank_name]] > $rank_name_max_length } {
set rank_name_max_length $length
}
}
set nick_max_length 0
set items_max_length 0
foreach chan [::tcl::dict::keys $::DuckHunt::player_data] {
foreach lower_nick [::tcl::dict::keys [::tcl::dict::get $::DuckHunt::player_data $chan]] {
if { [set nick_length [::tcl::string::length [::DuckHunt::get_data $lower_nick $chan "nick"]]] > $nick_max_length } {
set nick_max_length $nick_length
}
if { [set items_length [::tcl::string::length [::DuckHunt::get_data $lower_nick $chan "items"]]] > $items_max_length } {
set items_max_length $items_length
}
}
}
set counter 0
set msgcat_index 209 ; # index du 1er nom de colonne dans le pack de langue.
set default_column_width [list $nick_max_length 19 5 3 4 3 3 3 3 4 7 4 4 4 1 4 2 4 5 3 5 5 4 5 6 4 4 4 4 16 16 7 $rank_name_max_length $items_max_length] ; # Largeur minimale des colonnes, calculée en fonction de longueur maximum estimée de leur contenu.
foreach column {nick last_activity xp level xp_lvl_up ammo max_ammo ammo_clips max_clips accuracy effective_accuracy deflection armor jamming jammed jammed_nbr gun confisc ducks golden_ducks missed empty accidents wild_shots total_ammo shot_at neutralized deflected deaths best_time average_reflex_time karma rank items} {
lappend ul_width [set max [expr {max([lindex $default_column_width $counter],[::tcl::string::length [::msgcat::mc m$msgcat_index]])}]]
lappend col_width [expr {$max + 3}]
incr counter
incr msgcat_index
}
foreach chan [::tcl::dict::keys $::DuckHunt::player_data] {
puts $players_table_file_ID $chan
puts $players_table_file_ID [::tcl::string::repeat "-" [::tcl::string::length $chan]]
# Textes : "nom" "dern. activité" "xp" "lvl" "xp lvl sup." "mun." "mun. max." "charg." "charg. max." "préc. théorique" "préc. effective" "déflex." "armure" "enrayement" "enrayé" "nbr enrayements" "armé" "nbr confisc." "canards" "super-canards" "ratés" "tirs à vide" "accidents" "tirs sauvages" "mun. utilis." "tirs reçus" "tirs encaissés" "tirs déviés" "décès" "meilleur tps." "tps. réact. moyen" "karma" "rang" "objets spéciaux"
puts $players_table_file_ID [format $format_string [lindex $col_width 0] [::msgcat::mc m209] [lindex $col_width 1] [::msgcat::mc m210] [lindex $col_width 2] [::msgcat::mc m211] [lindex $col_width 3] [::msgcat::mc m212] [lindex $col_width 4] [::msgcat::mc m213] [lindex $col_width 5] [::msgcat::mc m214] [lindex $col_width 6] [::msgcat::mc m215] [lindex $col_width 7] [::msgcat::mc m216] [lindex $col_width 8] [::msgcat::mc m217] [lindex $col_width 9] [::msgcat::mc m218] [lindex $col_width 10] [::msgcat::mc m219] [lindex $col_width 11] [::msgcat::mc m220] [lindex $col_width 12] [::msgcat::mc m221] [lindex $col_width 13] [::msgcat::mc m222] [lindex $col_width 14] [::msgcat::mc m223] [lindex $col_width 15] [::msgcat::mc m224] [lindex $col_width 16] [::msgcat::mc m225] [lindex $col_width 17] [::msgcat::mc m226] [lindex $col_width 18] [::msgcat::mc m227] [lindex $col_width 19] [::msgcat::mc m228] [lindex $col_width 20] [::msgcat::mc m229] [lindex $col_width 21] [::msgcat::mc m230] [lindex $col_width 22] [::msgcat::mc m231] [lindex $col_width 23] [::msgcat::mc m232] [lindex $col_width 24] [::msgcat::mc m233] [lindex $col_width 25] [::msgcat::mc m234] [lindex $col_width 26] [::msgcat::mc m235] [lindex $col_width 27] [::msgcat::mc m236] [lindex $col_width 28] [::msgcat::mc m237] [lindex $col_width 29] [::msgcat::mc m238] [lindex $col_width 30] [::msgcat::mc m239] [lindex $col_width 31] [::msgcat::mc m240] [lindex $col_width 32] [::msgcat::mc m241] [lindex $col_width 33] [::msgcat::mc m242]]
puts $players_table_file_ID [format $format_string [lindex $col_width 0] [::tcl::string::repeat "-" [lindex $ul_width 0]] [lindex $col_width 1] [::tcl::string::repeat "-" [lindex $ul_width 1]] [lindex $col_width 2] [::tcl::string::repeat "-" [lindex $ul_width 2]] [lindex $col_width 3] [::tcl::string::repeat "-" [lindex $ul_width 3]] [lindex $col_width 4] [::tcl::string::repeat "-" [lindex $ul_width 4]] [lindex $col_width 5] [::tcl::string::repeat "-" [lindex $ul_width 5]] [lindex $col_width 6] [::tcl::string::repeat "-" [lindex $ul_width 6]] [lindex $col_width 7] [::tcl::string::repeat "-" [lindex $ul_width 7]] [lindex $col_width 8] [::tcl::string::repeat "-" [lindex $ul_width 8]] [lindex $col_width 9] [::tcl::string::repeat "-" [lindex $ul_width 9]] [lindex $col_width 10] [::tcl::string::repeat "-" [lindex $ul_width 10]] [lindex $col_width 11] [::tcl::string::repeat "-" [lindex $ul_width 11]] [lindex $col_width 12] [::tcl::string::repeat "-" [lindex $ul_width 12]] [lindex $col_width 13] [::tcl::string::repeat "-" [lindex $ul_width 13]] [lindex $col_width 14] [::tcl::string::repeat "-" [lindex $ul_width 14]] [lindex $col_width 15] [::tcl::string::repeat "-" [lindex $ul_width 15]] [lindex $col_width 16] [::tcl::string::repeat "-" [lindex $ul_width 16]] [lindex $col_width 17] [::tcl::string::repeat "-" [lindex $ul_width 17]] [lindex $col_width 18] [::tcl::string::repeat "-" [lindex $ul_width 18]] [lindex $col_width 19] [::tcl::string::repeat "-" [lindex $ul_width 19]] [lindex $col_width 20] [::tcl::string::repeat "-" [lindex $ul_width 20]] [lindex $col_width 21] [::tcl::string::repeat "-" [lindex $ul_width 21]] [lindex $col_width 22] [::tcl::string::repeat "-" [lindex $ul_width 22]] [lindex $col_width 23] [::tcl::string::repeat "-" [lindex $ul_width 23]] [lindex $col_width 24] [::tcl::string::repeat "-" [lindex $ul_width 24]] [lindex $col_width 25] [::tcl::string::repeat "-" [lindex $ul_width 25]] [lindex $col_width 26] [::tcl::string::repeat "-" [lindex $ul_width 26]] [lindex $col_width 27] [::tcl::string::repeat "-" [lindex $ul_width 27]] [lindex $col_width 28] [::tcl::string::repeat "-" [lindex $ul_width 28]] [lindex $col_width 29] [::tcl::string::repeat "-" [lindex $ul_width 29]] [lindex $col_width 30] [::tcl::string::repeat "-" [lindex $ul_width 30]] [lindex $col_width 31] [::tcl::string::repeat "-" [lindex $ul_width 31]] [lindex $col_width 32] [::tcl::string::repeat "-" [lindex $ul_width 32]] [lindex $col_width 33] [::tcl::string::repeat "-" [lindex $ul_width 33]]]
# Récupération des données et création du tableau.
foreach lower_nick [::tcl::dict::keys [::tcl::dict::get $::DuckHunt::player_data $chan]] {
lassign [::DuckHunt::get_level_and_grantings [::DuckHunt::get_data $lower_nick $chan "xp"]] level required_xp accuracy deflection defense jamming ammos_per_clip ammo_clips {} {} {}
set rank [::DuckHunt::lvl2rank $level]
foreach varname {gun jammed current_ammo_clip remaining_ammo_clips xp ducks_shot golden_ducks_shot missed_shots empty_shots humans_shot wild_shots bullets_received deflected_bullets deaths confiscated_weapons jammed_weapons best_time cumul_reflex_time nick items last_activity} {
set $varname [::DuckHunt::get_data $lower_nick $chan $varname]
}
set karma [::DuckHunt::calculate_karma $wild_shots $humans_shot $ducks_shot 1]
set total_ammo [expr {$ducks_shot + $missed_shots}]
set xp_to_lvlup [expr {$required_xp - $xp}]
if { $total_ammo != 0 } {
set effective_accuracy [::DuckHunt::format_floating_point_value [expr {(100.0 * $ducks_shot) / $total_ammo}] 2]
} else {
set effective_accuracy -1
}
set neutralized_bullets [expr {$bullets_received - $deaths - $deflected_bullets}]
if { $best_time == -1 } {
set best_time 9999999999
}
if { $ducks_shot != 0 } {
set average_reflex_time [format "%.3f" [expr {($cumul_reflex_time / 1000.0) / $ducks_shot}]]
} else {
set average_reflex_time "9999999999"
}
lappend data_table [list $nick $last_activity $xp $level $xp_to_lvlup $current_ammo_clip $ammos_per_clip $remaining_ammo_clips $ammo_clips $accuracy $effective_accuracy $deflection $defense $jamming $jammed $jammed_weapons $gun $confiscated_weapons $ducks_shot $golden_ducks_shot $missed_shots $empty_shots $humans_shot $wild_shots $total_ammo $bullets_received $neutralized_bullets $deflected_bullets $deaths $best_time $average_reflex_time $karma $rank $items]
}
# Tri du tableau.
switch -- $sort_by {
"nick" - "rank" {
# Par ordre alphabétique.
set data_table [lsort -dictionary -index [::tcl::dict::get $indexes $sort_by] [lsort -dictionary -index 0 $data_table]]
}
"best_time" - "average_reflex_time" {
# Par ordre numérique croissant.
set data_table [lsort -real -index [::tcl::dict::get $indexes $sort_by] [lsort -dictionary -index 0 $data_table]]
}
"items" {
# Par ordre alphabétique puis longueur de string décroissant
set data_table [lsort -decreasing -command {apply {{string_a string_b} {expr {[string length $string_a] - [string length $string_b]}}}} -index [::tcl::dict::get $indexes $sort_by] [lsort -dictionary -index [::tcl::dict::get $indexes $sort_by] [lsort -dictionary -index 0 $data_table]]]
}
default {
# Par ordre numérique décroissant.
set data_table [lsort -decreasing -real -index [::tcl::dict::get $indexes $sort_by] [lsort -dictionary -index 0 $data_table]]
}
}
# Ecriture du tableau dans le fichier.
foreach entry $data_table {
lassign $entry nick last_activity xp level xp_to_lvlup current_ammo_clip ammos_per_clip remaining_ammo_clips ammo_clips accuracy effective_accuracy deflection defense jamming jammed jammed_weapons gun confiscated_weapons ducks_shot golden_ducks_shot missed_shots empty_shots humans_shot wild_shots total_ammo bullets_received neutralized_bullets deflected_bullets deaths best_time average_reflex_time karma rank items
# Post-traitement des valeurs du tableau.
if { $effective_accuracy == -1 } {
set effective_accuracy "-"
} else {
set effective_accuracy "${effective_accuracy}%"
}
if { $best_time == 9999999999 } {
set best_time "-"
} else {
set best_time [::DuckHunt::adapt_time_resolution [::tcl::string::map {"." ""} $best_time] 1]
}
if { $average_reflex_time == 9999999999 } {
set average_reflex_time "-"
} else {
set average_reflex_time [::DuckHunt::adapt_time_resolution [::tcl::string::map {"." ""} $average_reflex_time] 1]
}
if { $last_activity == -1 } {
set last_activity "-"
} else {
set last_activity [strftime [::msgcat::mc m422] $last_activity]
}
puts $players_table_file_ID [format $format_string [lindex $col_width 0] $nick [lindex $col_width 1] $last_activity [lindex $col_width 2] $xp [lindex $col_width 3] $level [lindex $col_width 4] $xp_to_lvlup [lindex $col_width 5] $current_ammo_clip [lindex $col_width 6] $ammos_per_clip [lindex $col_width 7] $remaining_ammo_clips [lindex $col_width 8] $ammo_clips [lindex $col_width 9] "${accuracy}%" [lindex $col_width 10] $effective_accuracy [lindex $col_width 11] "${deflection}%" [lindex $col_width 12] "${defense}%" [lindex $col_width 13] "${jamming}%" [lindex $col_width 14] $jammed [lindex $col_width 15] $jammed_weapons [lindex $col_width 16] $gun [lindex $col_width 17] $confiscated_weapons [lindex $col_width 18] $ducks_shot [lindex $col_width 19] $golden_ducks_shot [lindex $col_width 20] $missed_shots [lindex $col_width 21] $empty_shots [lindex $col_width 22] $humans_shot [lindex $col_width 23] $wild_shots [lindex $col_width 24] $total_ammo [lindex $col_width 25] $bullets_received [lindex $col_width 26] $neutralized_bullets [lindex $col_width 27] $deflected_bullets [lindex $col_width 28] $deaths [lindex $col_width 29] $best_time [lindex $col_width 30] $average_reflex_time [lindex $col_width 31] $karma [lindex $col_width 32] $rank [lindex $col_width 33] $items]
}
# On saute 2 lignes entre chaque chan.
puts $players_table_file_ID ""
puts $players_table_file_ID ""
unset data_table
}
}
close $players_table_file_ID
::DuckHunt::purge_db_from_memory
# Message : "Un rapport a été généré à l'emplacement %s"
::DuckHunt::display_output help NOTICE $src_nick [::msgcat::mc m262 $::DuckHunt::players_table_file]
}
}
###############################################################################
### Redonne des chargeurs à tout le monde chaque jour à minuit.
###############################################################################
proc ::DuckHunt::refill_ammo {args} {
::DuckHunt::read_database
foreach chan [::tcl::dict::keys $::DuckHunt::player_data] {
foreach lower_nick [::tcl::dict::keys [::tcl::dict::get $::DuckHunt::player_data $chan]] {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "remaining_ammo_clips" [lindex [::DuckHunt::get_level_and_grantings [::DuckHunt::get_data $lower_nick $chan "xp"]] 7]
}
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan [unixtime] - - - - "refill_ammo" 0 -
}
}
::DuckHunt::write_database
::DuckHunt::purge_db_from_memory
}
###############################################################################
### Rend les armes confisquées.
###############################################################################
proc ::DuckHunt::hand_back_weapons {args} {
if { ![::tcl::info::exists ::DuckHunt::player_data] } {
set must_unload_database 1
::DuckHunt::read_database
} else {
set must_unload_database 0
}
# Sur un chan seulement.
if { [llength $args] == 1 } {
set chans_to_process [join $args]
# Sur tous les chans.
} else {
set chans_to_process [::tcl::dict::keys $::DuckHunt::player_data]
}
foreach chan $chans_to_process {
foreach lower_nick [::tcl::dict::keys [::tcl::dict::get $::DuckHunt::player_data $chan]] {
if { [::DuckHunt::get_data $lower_nick $chan "gun"] == 0 } {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "gun" 1
}
}
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan [unixtime] - - - - "hand_back_weapons" 0 -
}
}
# Remarque : on ne décharge pas la db de la mémoire si la procédure a été
# appelée après un envol de canard en raison de gun_hand_back_mode = 2.
if { $must_unload_database } {
::DuckHunt::write_database
::DuckHunt::purge_db_from_memory
}
}
###############################################################################
### On vérifie si un joueur est déjà enregistré dans la base de données et
### on l'initialise si nécessaire.
###############################################################################
proc ::DuckHunt::initialize_player {nick lower_nick chan} {
if {
!([::tcl::dict::exists $::DuckHunt::player_data $chan])
|| !([::tcl::dict::exists $::DuckHunt::player_data $chan $lower_nick])
} then {
lassign [::DuckHunt::get_level_and_grantings 0] {} {} {} {} {} {} default_ammos_in_clip default_ammo_clips_per_day {} {} {} {}
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick [::tcl::dict::create "gun" 1 "jammed" 0 "current_ammo_clip" $default_ammos_in_clip "remaining_ammo_clips" $default_ammo_clips_per_day "xp" 0 "ducks_shot" 0 "missed_shots" 0 "empty_shots" 0 "humans_shot" 0 "wild_shots" 0 "bullets_received" 0 "deflected_bullets" 0 "deaths" 0 "confiscated_weapons" 0 "jammed_weapons" 0 "best_time" -1 "cumul_reflex_time" 0 "nick" $nick "items" {} "golden_ducks_shot" 0 "last_activity" [unixtime]]
}
}
###############################################################################
### Retourne la valeur d'une donnée utilisateur.
###############################################################################
proc ::DuckHunt::get_data {lower_nick chan field_name} {
# On élimine les items éventuellement expirés de l'inventaire du joueur avant
# de retourner l'information.
if {
([::tcl::dict::exists $::DuckHunt::player_data $chan])
&& ([::tcl::dict::exists $::DuckHunt::player_data $chan $lower_nick])
&& ($field_name eq "items")
} then {
set expired_items_found 0
set item_index 0
foreach item [set items [::tcl::dict::get $::DuckHunt::player_data $chan $lower_nick "items"]] {
if {
([set expiration_date [lindex $item 0]] ne "-")
&& ($expiration_date < [unixtime])
} then {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [lreplace $items $item_index $item_index]
set expired_items_found 1
} else {
incr item_index
}
}
if { $expired_items_found } {
::DuckHunt::write_database
}
}
# Récupération de l'information demandée.
if {
([::tcl::dict::exists $::DuckHunt::player_data $chan])
&& ([::tcl::dict::exists $::DuckHunt::player_data $chan $lower_nick])
} then {
return [::tcl::dict::get $::DuckHunt::player_data $chan $lower_nick $field_name]
} else {
lassign [::DuckHunt::get_level_and_grantings 0] {} {} {} {} {} {} default_ammos_in_clip default_ammo_clips_per_day {} {} {} {}
switch -- $field_name {
"gun" { return 1 }
"jammed" - "xp" - "ducks_shot" - "golden_ducks_shot" - "missed_shots" - "empty_shots" - "humans_shot" - "wild_shots" - "bullets_received" - "deflected_bullets" - "deaths" - "confiscated_weapons" - "jammed_weapons" - "cumul_reflex_time" { return 0 }
"current_ammo_clip" { return $default_ammos_in_clip }
"remaining_ammo_clips" { return $default_ammo_clips_per_day }
"best_time" { return -1 }
"rank" { return [::DuckHunt::lvl2rank 1] }
"nick" {
# Correction de la casse du nick
if { [onchan $lower_nick $chan] } {
set nick [lindex [set nick_list [chanlist $chan]] [lsearch -nocase -exact $nick_list $lower_nick]]
} else {
set nick $lower_nick
}
return $nick
}
"items" { return "" }
"last_activity" { return "-1" }
}
}
}
###############################################################################
### Incrémente ou décrémente la valeur d'une donnée utilisateur.
###############################################################################
proc ::DuckHunt::incr_data {lower_nick chan field_name increment} {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick $field_name [expr {[::DuckHunt::get_data $lower_nick $chan $field_name] + $increment}]
}
###############################################################################
### Récupère des informations sur un item possédé par un joueur.
### Retourne $item_index $expiration_date $data
###############################################################################
proc ::DuckHunt::get_item_info {lower_nick chan item_id} {
if { [set item_index [lsearch -index 1 [::DuckHunt::get_data $lower_nick $chan "items"] "$item_id"]] != -1 } {
lassign [lindex [::DuckHunt::get_data $lower_nick $chan "items"] $item_index] expiration_date {} data
return [list $item_index $expiration_date $data]
} else {
return {-1 0 "-"}
}
}
###############################################################################
### Décrémente le compteur d'utilisations d'un item.
###############################################################################
proc ::DuckHunt::decrement_item_uses {lower_nick chan item_id item_index expiration_date item_uses} {
incr item_uses -1
if { $item_uses > 0 } {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [lreplace [::DuckHunt::get_data $lower_nick $chan "items"] $item_index $item_index [list $expiration_date $item_id $item_uses]]
} else {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" [lreplace [::DuckHunt::get_data $lower_nick $chan "items"] $item_index $item_index]
}
}
###############################################################################
### Retourne une liste contenant différentes informations en fonction d'un
### nombre de points d'xp :
### {level required_xp précision déflexion défense enrayement ammos_per_clip ammo_clips xp_miss xp_wild_fire xp_accident}
###############################################################################
proc ::DuckHunt::get_level_and_grantings {xp} {
foreach level [lsort -integer [array names ::DuckHunt::level_grantings]] {
lassign [split $::DuckHunt::level_grantings($level) ","] required_xp accuracy deflection defense jamming ammo_clip_size ammo_clips xp_miss xp_wild_fire xp_accident
if { $xp >= $required_xp } {
continue
} else {
return [list $level $required_xp [expr $accuracy] [expr $deflection] [expr $defense] [expr $jamming] [expr $ammo_clip_size] [expr $ammo_clips] $xp_miss $xp_wild_fire $xp_accident]
}
}
}
###############################################################################
### Recalcule le nombre de munitions dans l'arme et de chargeurs restants lors
### d'un changement de niveau du joueur afin que les valeurs ne dépassent pas
### les capacités de l'arme.
###############################################################################
proc ::DuckHunt::recalculate_ammo_on_lvl_change {lower_nick chan} {
lassign [::DuckHunt::get_level_and_grantings [::DuckHunt::get_data $lower_nick $chan "xp"]] {} {} {} {} {} {} default_ammos_in_clip default_ammo_clips_per_day {} {} {} {}
if { [::DuckHunt::get_data $lower_nick $chan "current_ammo_clip"] > $default_ammos_in_clip } {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "current_ammo_clip" $default_ammos_in_clip
}
if { [::DuckHunt::get_data $lower_nick $chan "remaining_ammo_clips"] > $default_ammo_clips_per_day } {
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "remaining_ammo_clips" $default_ammo_clips_per_day
}
}
###############################################################################
### Cherche et élimine les morceaux de pain expirés
###############################################################################
proc ::DuckHunt::check_for_expired_pieces_of_bread {args} {
foreach chan [channels] {
if { [llength [set pieces_of_bread [channel get $chan DuckHunt-PiecesOfBread]]] != 0 } {
set item_index 0
foreach expiration_date $pieces_of_bread {
if { $expiration_date < [unixtime] } {
channel set $chan DuckHunt-PiecesOfBread [lreplace [channel get $chan DuckHunt-PiecesOfBread] $item_index $item_index]
if { $::DuckHunt::method == 2 } {
::DuckHunt::replan_flights - - - $chan "@"
}
} else {
incr item_index
}
}
}
}
}
###############################################################################
### Convertit un niveau en rang
###############################################################################
proc ::DuckHunt::lvl2rank {level} {
return [lindex [::msgcat::mc m134] $level]
}
###############################################################################
### Retourne le pourcentage de chances pour qu'un tir manqué touche quelqu'un
### en fonction du nombre d'utilisateurs sur le chan.
###############################################################################
proc ::DuckHunt::determine_chances_to_hit_someone_else {chan duck_present} {
# On détermine les chances pour qu'un tir manqué touche quelqu'un par accident
# en fonction du nombre d'utilisateurs sur le chan.
set num_users [llength [chanlist $chan]]
# Si un canard est en vol.
if { $duck_present } {
if { $num_users <= 10 } {
return $::DuckHunt::chances_to_hit_someone_else_1_10
} elseif { $num_users <=20 } {
return $::DuckHunt::chances_to_hit_someone_else_11_20
} elseif { $num_users <=30 } {
return $::DuckHunt::chances_to_hit_someone_else_21_30
} else {
return $::DuckHunt::chances_to_hit_someone_else_31_
}
# S'il n'y a aucun canard en vol.
} else {
if { $num_users <= 10 } {
return $::DuckHunt::chances_wild_fire_hit_someone_1_10
} elseif { $num_users <=20 } {
return $::DuckHunt::chances_wild_fire_hit_someone_11_20
} elseif { $num_users <=30 } {
return $::DuckHunt::chances_wild_fire_hit_someone_21_30
} else {
return $::DuckHunt::chances_wild_fire_hit_someone_31_
}
}
}
###############################################################################
### Retourne le nick d'un utilisateur choisi aléatoirement sur $chan et ne
### pouvant être ni le nick de l'Eggdrop, ni le nick de l'utilisateur d'où
### provient la balle.
###############################################################################
proc ::DuckHunt::random_user {chan source_nick} {
if { $::DuckHunt::exempted_flags eq "" } {
set user_list [lsearch -all -exact -not -inline [lsearch -all -exact -not -inline [chanlist $chan] $::nick] $source_nick]
} else {
set user_list [lsearch -all -exact -not -inline [lsearch -all -exact -not -inline [chanlist $chan "-${::DuckHunt::exempted_flags}&-${::DuckHunt::exempted_flags}"] $::nick] $source_nick]
}
if { $user_list eq "" } {
return "@nobody@"
} else {
if { $::DuckHunt::only_hunters_can_be_shot } {
set hunters_list [::tcl::dict::keys [::tcl::dict::get $::DuckHunt::player_data $chan]]
foreach hunter $hunters_list {
if {
([set index [lsearch -nocase -exact $user_list [set lower_hunter_nick [::tcl::string::tolower $hunter]]]] != -1)
&& ([expr {[::DuckHunt::get_data $lower_hunter_nick $chan "ducks_shot"] + [::DuckHunt::get_data $lower_hunter_nick $chan "missed_shots"]}] > 0)
} then {
lappend tmp_user_list [lindex $user_list $index]
}
}
if { [::tcl::info::exists tmp_user_list] } {
set user_list $tmp_user_list
} else {
return "@nobody@"
}
}
return [lindex $user_list [rand [llength $user_list]]]
}
}
###############################################################################
### Ajoute des informations au rapport d'administration journalier.
### action peut valoir : soaring shoot reload miss jam die hit deflect wild_fire
###############################################################################
proc ::DuckHunt::add_to_log {chan timestamp hunter_nick lower_hunter_nick target_nick shooting_time action noLF extra_data} {
if { $action in {hit_golden_duck shoot shoot_golden_duck miss empty_shot reload jam unjam unjam_reload accident wild_fire} } {
lassign [::DuckHunt::get_level_and_grantings [::DuckHunt::get_data $lower_hunter_nick $chan "xp"]] {} {} {} {} {} {} ammos_per_clip ammo_clips
set ammo [regsub -all "\017" [stripcodes abcgru [::DuckHunt::display_ammo $lower_hunter_nick $chan $ammos_per_clip]] ""]
set clips [regsub -all "\017" [stripcodes abcgru [::DuckHunt::display_clips $lower_hunter_nick $chan $ammo_clips]] ""]
}
set logfile_ID [open "${::DuckHunt::log_directory}${chan}_[strftime "%Y%m%d" [unixtime]].log" a+]
switch -- $action {
"soaring" {
# Message : "\[%s\] \\_O< *COIN*"
puts -nonewline $logfile_ID [::msgcat::mc m157 [strftime "%H:%M:%S" $timestamp]]
}
"golden_duck_soaring" {
# Message : "\[%s\] \\_O< *COIN* \[SUPER-CANARD\]"
puts -nonewline $logfile_ID [::msgcat::mc m254 [strftime "%H:%M:%S" $timestamp]]
}
"fake_duck_soaring" {
# Message : "\[%s\] \\_O< *COIN* \[canard mécanique\]"
puts -nonewline $logfile_ID [::msgcat::mc m352 [strftime "%H:%M:%S" $timestamp]]
}
"launch" {
# Message : "\[%s\] \\_O< *COIN* (lancement manuel par %s)"
puts -nonewline $logfile_ID [::msgcat::mc m158 [strftime "%H:%M:%S" $timestamp] $hunter_nick]
}
"golden_duck_launch" {
# Message : "\[%s\] \\_O< *COIN* \[SUPER-CANARD\] (lancement manuel par %s)"
puts -nonewline $logfile_ID [::msgcat::mc m255 [strftime "%H:%M:%S" $timestamp] $hunter_nick]
}
"frightened" {
# Message : "\[%s\] ·°'`'°-.,¸¸.·°'` canard effrayé"
puts -nonewline $logfile_ID [::msgcat::mc m159 [strftime "%H:%M:%S" $timestamp]]
}
"escaped" {
# Message : "\[%s\] ·°'`'°-.,¸¸.·°'` canard parti"
puts -nonewline $logfile_ID [::msgcat::mc m160 [strftime "%H:%M:%S" $timestamp]]
}
"golden_duck_escaped" {
# Message : "\[%s\] ·°'`'°-.,¸¸.·°'` super-canard parti"
puts -nonewline $logfile_ID [::msgcat::mc m276 [strftime "%H:%M:%S" $timestamp]]
}
"fake_duck_escaped" {
# Message : "\[%s\] ·°'`'°-.,¸¸.·°'` canard mécanique parti"
puts -nonewline $logfile_ID [::msgcat::mc m355 [strftime "%H:%M:%S" $timestamp]]
}
"hit_golden_duck" {
# Message : " |-- \[%s\] %s (%s|%s) *BANG* \\_O< *CHTOK* \[SUPER-CANARD\]"
puts -nonewline $logfile_ID [::msgcat::mc m256 [strftime "%H:%M:%S" $timestamp] $hunter_nick $ammo $clips]
}
"shoot" {
# Message : " |-- \[%s\] %s (%s|%s) *BANG* \\_X< *COUAC* (%s %s / %s)"
puts -nonewline $logfile_ID [::msgcat::mc m161 [strftime "%H:%M:%S" $timestamp] $hunter_nick $ammo $clips [::DuckHunt::get_data $lower_hunter_nick $chan "ducks_shot"] [::DuckHunt::plural [::DuckHunt::get_data $lower_hunter_nick $chan "ducks_shot"] [::msgcat::mc m27] [::msgcat::mc m28]] $shooting_time]
}
"shoot_golden_duck" {
# Message : " |-- \[%s\] %s (%s|%s) *BANG* \\_X< *COUAC* \[SUPER-CANARD\] (%s %s / %s)"
puts -nonewline $logfile_ID [::msgcat::mc m257 [strftime "%H:%M:%S" $timestamp] $hunter_nick $ammo $clips [::DuckHunt::get_data $lower_hunter_nick $chan "ducks_shot"] [::DuckHunt::plural [::DuckHunt::get_data $lower_hunter_nick $chan "ducks_shot"] [::msgcat::mc m27] [::msgcat::mc m28]] $shooting_time]
}
"miss" {
# Message : " |-- \[%s\] %s (%s|%s) *BANG*"
puts -nonewline $logfile_ID [::msgcat::mc m162 [strftime "%H:%M:%S" $timestamp] $hunter_nick $ammo $clips]
}
"empty_shot" {
# Message : " |-- \[%s\] %s (%s|%s) *CLIC*"
puts -nonewline $logfile_ID [::msgcat::mc m163 [strftime "%H:%M:%S" $timestamp] $hunter_nick $ammo $clips]
}
"reload" {
# Message : " |-- \[%s\] %s (%s|%s) *CLAC CLAC*"
puts -nonewline $logfile_ID [::msgcat::mc m164 [strftime "%H:%M:%S" $timestamp] $hunter_nick $ammo $clips]
}
"jam" {
# Message : " |-- \[%s\] %s (%s|%s) *CLAC*"
puts -nonewline $logfile_ID [::msgcat::mc m165 [strftime "%H:%M:%S" $timestamp] $hunter_nick $ammo $clips]
}
"unjam" {
# Message : " |-- \[%s\] %s (%s|%s) *Crr..CLIC*"
puts -nonewline $logfile_ID [::msgcat::mc m166 [strftime "%H:%M:%S" $timestamp] $hunter_nick $ammo $clips]
}
"unjam_reload" {
# Message : " |-- \[%s\] %s (%s|%s) *Crr..CLIC* *CLAC CLAC*"
puts -nonewline $logfile_ID [::msgcat::mc m167 [strftime "%H:%M:%S" $timestamp] $hunter_nick $ammo $clips]
}
"accident" {
# Message : " |-- \[%s\] %s (%s|%s) *BANG* accident :"
puts -nonewline $logfile_ID [::msgcat::mc m168 [strftime "%H:%M:%S" $timestamp] $hunter_nick $ammo $clips]
}
"die" {
# Message : " %s *ARG*"
puts -nonewline $logfile_ID [::msgcat::mc m169 $target_nick]
}
"hit" {
# Message : " %s *CHTOK*"
puts -nonewline $logfile_ID [::msgcat::mc m170 $target_nick]
}
"deflect" {
# Message : " %s *PIEWWW*"
puts -nonewline $logfile_ID [::msgcat::mc m171 $target_nick]
}
"wild_fire" {
# Message : " |-- \[%s\] %s (%s|%s) *BANG* sauvage"
puts -nonewline $logfile_ID [::msgcat::mc m172 [strftime "%H:%M:%S" $timestamp] $hunter_nick $ammo $clips]
}
"confiscated" {
# Message : " ---> arme confisquée"
puts -nonewline $logfile_ID [::msgcat::mc m173]
}
"dead_duck" {
# Message : " \\_X< *COUAC*"
puts -nonewline $logfile_ID [::msgcat::mc m174]
}
"dead_golden_duck" {
# Message : " \\_X< *COUAC* \[SUPER-CANARD\]"
puts -nonewline $logfile_ID [::msgcat::mc m258]
}
"dead_fake_duck" {
# Message : " \\_X< *BZZzZzt* \[canard mécanique\]"
puts -nonewline $logfile_ID [::msgcat::mc m360]
}
"refill_ammo" {
# Message : "\[%s\] ravitaillement en munitions"
puts -nonewline $logfile_ID [::msgcat::mc m175 [strftime "%H:%M:%S" $timestamp]]
}
"hand_back_weapons" {
# Message : "\[%s\] restitution des armes confisquées"
puts -nonewline $logfile_ID [::msgcat::mc m176 [strftime "%H:%M:%S" $timestamp]]
}
"perm_unarm" {
# Message : "\[%s\] désarmement permanent de %s par %s"
puts -nonewline $logfile_ID [::msgcat::mc m177 [strftime "%H:%M:%S" $timestamp] $target_nick $hunter_nick]
}
"temp_unarm" {
# Message : "\[%s\] désarmement temporaire de %s par %s"
puts -nonewline $logfile_ID [::msgcat::mc m178 [strftime "%H:%M:%S" $timestamp] $target_nick $hunter_nick]
}
"rearm" {
# Message : "\[%s\] réarmement de %s par %s"
puts -nonewline $logfile_ID [::msgcat::mc m179 [strftime "%H:%M:%S" $timestamp] $target_nick $hunter_nick]
}
"item_1" {
# Message : "\[%s\] \[\$\$\$\] %s achète une balle (%s xp)"
puts -nonewline $logfile_ID [::msgcat::mc m301 [strftime "%H:%M:%S" $timestamp] $hunter_nick $::DuckHunt::extra_ammo_cost]
}
"item_2" {
# Message : "\[%s\] \[\$\$\$\] %s achète un chargeur (%s xp)"
puts -nonewline $logfile_ID [::msgcat::mc m302 [strftime "%H:%M:%S" $timestamp] $hunter_nick $::DuckHunt::extra_clip_cost]
}
"item_3" {
# Message : "\[%s\] \[\$\$\$\] %s achète des munitions AP (%s xp)"
puts -nonewline $logfile_ID [::msgcat::mc m303 [strftime "%H:%M:%S" $timestamp] $hunter_nick $::DuckHunt::AP_ammo_cost]
}
"item_4" {
# Message : "\[%s\] \[\$\$\$\] %s achète des munitions explosives (%s xp)"
puts -nonewline $logfile_ID [::msgcat::mc m304 [strftime "%H:%M:%S" $timestamp] $hunter_nick $::DuckHunt::explosive_ammo_cost]
}
"item_5" {
# Message : "\[%s\] \[\$\$\$\] %s rachète son arme confisquée (%s xp)"
puts -nonewline $logfile_ID [::msgcat::mc m305 [strftime "%H:%M:%S" $timestamp] $hunter_nick $::DuckHunt::hand_back_confiscated_weapon_cost]
}
"item_6" {
# Message : "\[%s\] \[\$\$\$\] %s achète de la graisse (%s xp)"
puts -nonewline $logfile_ID [::msgcat::mc m306 [strftime "%H:%M:%S" $timestamp] $hunter_nick $::DuckHunt::grease_cost]
}
"item_7" {
# Message : "\[%s\] \[\$\$\$\] %s achète une lunette de visée (%s xp)"
puts -nonewline $logfile_ID [::msgcat::mc m307 [strftime "%H:%M:%S" $timestamp] $hunter_nick $::DuckHunt::sight_cost]
}
"item_8" {
# Message : "\[%s\] \[\$\$\$\] %s achète un détecteur infrarouge (%s xp)"
puts -nonewline $logfile_ID [::msgcat::mc m308 [strftime "%H:%M:%S" $timestamp] $hunter_nick $::DuckHunt::infrared_detector_cost]
}
"item_9" {
# Message : "\[%s\] \[\$\$\$\] %s achète un silencieux (%s xp)"
puts -nonewline $logfile_ID [::msgcat::mc m309 [strftime "%H:%M:%S" $timestamp] $hunter_nick $::DuckHunt::silencer_cost]
}
"item_10" {
# Message : "\[%s\] \[\$\$\$\] %s achète un trèfle à 4 feuilles +%s (%s xp)"
puts -nonewline $logfile_ID [::msgcat::mc m310 [strftime "%H:%M:%S" $timestamp] $hunter_nick $extra_data $::DuckHunt::four_leaf_clover_cost]
}
"item_11" {
# Message : "\[%s\] \[\$\$\$\] %s achète des lunettes de soleil (%s xp)"
puts -nonewline $logfile_ID [::msgcat::mc m311 [strftime "%H:%M:%S" $timestamp] $hunter_nick $::DuckHunt::sunglasses_cost]
}
"item_12" {
# Message : "\[%s\] \[\$\$\$\] %s achète des vêtements de rechange (%s xp)"
puts -nonewline $logfile_ID [::msgcat::mc m312 [strftime "%H:%M:%S" $timestamp] $hunter_nick $::DuckHunt::spare_clothes_cost]
}
"item_13" {
# Message : "\[%s\] \[\$\$\$\] %s achète un goupillon (%s xp)"
puts -nonewline $logfile_ID [::msgcat::mc m313 [strftime "%H:%M:%S" $timestamp] $hunter_nick $::DuckHunt::brush_for_weapon_cost]
}
"item_14" {
# Message : "\[%s\] \[\$\$\$\] %s achète un miroir pour %s (%s xp)"
puts -nonewline $logfile_ID [::msgcat::mc m314 [strftime "%H:%M:%S" $timestamp] $hunter_nick $target_nick $::DuckHunt::mirror_cost]
}
"item_15" {
# Message : "\[%s\] \[\$\$\$\] %s achète une poignée de sable pour %s (%s xp)"
puts -nonewline $logfile_ID [::msgcat::mc m315 [strftime "%H:%M:%S" $timestamp] $hunter_nick $target_nick $::DuckHunt::sand_cost]
}
"item_16" {
# Message : "\[%s\] \[\$\$\$\] %s achète un seau d'eau pour %s (%s xp)"
puts -nonewline $logfile_ID [::msgcat::mc m316 [strftime "%H:%M:%S" $timestamp] $hunter_nick $target_nick $::DuckHunt::water_bucket_cost]
}
"item_17" {
# Message : "\[%s\] \[\$\$\$\] %s achète un sabotage pour %s (%s xp)"
puts -nonewline $logfile_ID [::msgcat::mc m317 [strftime "%H:%M:%S" $timestamp] $hunter_nick $target_nick $::DuckHunt::sabotage_cost]
}
"item_18" {
# Message : "\[%s\] \[\$\$\$\] %s achète une assurance vie (%s xp)"
puts -nonewline $logfile_ID [::msgcat::mc m318 [strftime "%H:%M:%S" $timestamp] $hunter_nick $::DuckHunt::life_insurance_cost]
}
"item_19" {
# Message : "\[%s\] \[\$\$\$\] %s achète une assurance responsabilité civile (%s xp)"
puts -nonewline $logfile_ID [::msgcat::mc m319 [strftime "%H:%M:%S" $timestamp] $hunter_nick $::DuckHunt::liability_insurance_cost]
}
"item_20" {
# Message : "\[%s\] \[\$\$\$\] %s achète un appeau (%s xp)"
puts -nonewline $logfile_ID [::msgcat::mc m320 [strftime "%H:%M:%S" $timestamp] $hunter_nick $::DuckHunt::decoy_cost]
}
"item_21" {
# Message : "\[%s\] \[\$\$\$\] %s achète un morceau de pain (%s xp)"
puts -nonewline $logfile_ID [::msgcat::mc m321 [strftime "%H:%M:%S" $timestamp] $hunter_nick $::DuckHunt::piece_of_bread_cost]
}
"item_22" {
# Message : "\[%s\] \[\$\$\$\] %s achète un détecteur de canards (%s xp)"
puts -nonewline $logfile_ID [::msgcat::mc m322 [strftime "%H:%M:%S" $timestamp] $hunter_nick $::DuckHunt::duck_detector_cost]
}
"item_23" {
# Message : "\[%s\] \[\$\$\$\] %s achète un canard mécanique (%s xp)"
puts -nonewline $logfile_ID [::msgcat::mc m323 [strftime "%H:%M:%S" $timestamp] $hunter_nick $::DuckHunt::fake_duck_cost]
}
"merge_stats_1" {
lassign $extra_data src_stats dst_stats resulting_stats
# Message : "\[%s\] \[Transfert de stats\] renommage chasseur vers chasseur désarmé : %s \{%s\} + %s \{%s\} = %s \{%s\}"
puts -nonewline $logfile_ID [::msgcat::mc m389 [strftime "%H:%M:%S" $timestamp] $hunter_nick $src_stats $target_nick $dst_stats $target_nick $resulting_stats]
}
"merge_stats_2" {
lassign $extra_data src_stats dst_stats resulting_stats
# Message : "\[%s\] \[Transfert de stats\] renommage chasseur désarmé vers chasseur désarmé : %s \{%s\} + %s \{%s\} = %s \{%s\}"
puts -nonewline $logfile_ID [::msgcat::mc m390 [strftime "%H:%M:%S" $timestamp] $hunter_nick $src_stats $target_nick $dst_stats $target_nick $resulting_stats]
}
"merge_stats_3" {
lassign $extra_data src_stats dst_stats resulting_stats
# Message : "\[%s\] \[Transfert de stats\] renommage non-chasseur vers chasseur : %s \{%s\} + %s \{%s\} = %s \{%s\}"
puts -nonewline $logfile_ID [::msgcat::mc m391 [strftime "%H:%M:%S" $timestamp] $hunter_nick $src_stats $target_nick $dst_stats $target_nick $resulting_stats]
}
"merge_stats_4" {
lassign $extra_data src_stats dst_stats resulting_stats
# Message : "\[%s\] \[Transfert de stats\] renommage chasseur vers chasseur : %s \{%s\} + %s \{%s\} = %s \{%s\}"
puts -nonewline $logfile_ID [::msgcat::mc m392 [strftime "%H:%M:%S" $timestamp] $hunter_nick $src_stats $target_nick $dst_stats $target_nick $resulting_stats]
}
"merge_stats_5" {
lassign $extra_data src_stats dst_stats resulting_stats
# Message : "\[%s\] \[Transfert de stats\] fusion manuelle : %s \{%s\} + %s \{%s\} = %s \{%s\}"
puts -nonewline $logfile_ID [::msgcat::mc m420 [strftime "%H:%M:%S" $timestamp] $hunter_nick $src_stats $target_nick $dst_stats $target_nick $resulting_stats]
}
"drop" {
# Message : " |-- \[%s\] %s \[drop : %s\]"
puts -nonewline $logfile_ID [::msgcat::mc m419 [strftime "%H:%M:%S" $timestamp] $hunter_nick $extra_data]
}
}
if { !$noLF } {
puts -nonewline $logfile_ID "\n"
}
close $logfile_ID
}
###############################################################################
### Lecture de la base de données.
###############################################################################
proc ::DuckHunt::read_database {} {
incr ::DuckHunt::db_sessions
if { [file exists $::DuckHunt::db_file] } {
set dbfile_ID [open $::DuckHunt::db_file r]
set bad_header 0
gets $dbfile_ID 1st_line
# Si l'en-tête n'existe pas, on note de réécrire la base de données pour y
# remédier.
if { ![::tcl::string::match "--- *" $1st_line] } {
set bad_header 1
seek $dbfile_ID 0
} else {
# Si l'en-tête ne correspond pas à la version actuelle, on note de réécrire
# la base de données pour y remédier.
if { [lindex [regexp -inline {v([^\s]+)} $1st_line] 1] != $::DuckHunt::version } {
set bad_header 1
}
# On passe l'en-tête.
while { [gets $dbfile_ID] != "---" } {
continue
}
}
set ::DuckHunt::player_data [read -nonewline $dbfile_ID]
# On vérifie si la base de données est d'un format antérieur à la v2 et
# nécessite d'être convertie.
foreach chan [::tcl::dict::keys $::DuckHunt::player_data] {
foreach lower_nick [::tcl::dict::keys [::tcl::dict::get $::DuckHunt::player_data $chan]] {
# Si le nombre de champs d'une entrée = 21 alors la conversion n'est
# pas nécessaire.
if { [llength [::tcl::dict::keys [::tcl::dict::get $::DuckHunt::player_data $chan $lower_nick]]] == 21 } {
set converted_db_from_v1 0
break
} else {
if { ![::tcl::info::exists converted_db_from_v1] } {
# Message : "\00314\[%s\]\017 Une base de données d'un ancien format a été détectée. Conversion en cours..."
::DuckHunt::display_output loglev - - [::msgcat::mc m260 $::DuckHunt::scriptname]
set bad_header 1
}
set converted_db_from_v1 1
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "items" {}
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "golden_ducks_shot" 0
::tcl::dict::set ::DuckHunt::player_data $chan $lower_nick "last_activity" -1
}
}
if { !$converted_db_from_v1 } {
break
}
}
if { $bad_header } {
::DuckHunt::write_database
# Message : "\00314\[%s\]\017 La base de données a été mise à jour avec succès à la version %s."
::DuckHunt::display_output loglev - - [::msgcat::mc m83 $::DuckHunt::scriptname $::DuckHunt::version]
}
close $dbfile_ID
} else {
set ::DuckHunt::player_data ""
}
}
###############################################################################
### Ecriture de la base de données.
###############################################################################
proc ::DuckHunt::write_database {} {
set dbfile_ID [open $::DuckHunt::db_file w]
# Texte : "--- %s v%s - Base de données contenant les données et les statistiques des participants ---"
puts $dbfile_ID [::msgcat::mc m84 $::DuckHunt::scriptname $::DuckHunt::version]
# Texte : "--- structure : #chan1 {player1 {gun jammed current_ammo_clip remaining_ammo_clips xp ducks_shot missed_shots empty_shots humans_shot wild_shots bullets_received deflected_bullets deaths confiscated_weapons jammed_weapons best_time cumul_reflex_time nick items golden_ducks last_activity} player2 {...}} #chan2 {...}"
puts $dbfile_ID [::msgcat::mc m85]
# Texte : --- gun : 1 = armé, 0 = désarmement temporaire, -1 = désarmement permanent.
puts $dbfile_ID [::msgcat::mc m86]
# Texte : "--- jammed : Peut valoir 1 ou 0 selon que l'arme est enrayée ou non."
puts $dbfile_ID [::msgcat::mc m87]
# Texte : "--- current_ammo_clip : Nombre de munitions restantes dans l'arme (-1 = illimité)."
puts $dbfile_ID [::msgcat::mc m88]
# Texte : "--- remaining_ammo_clips : Nombre de chargeurs restants (-1 = illimité)."
puts $dbfile_ID [::msgcat::mc m89]
# Texte : "--- xp : Nombre de points d'expérience acquis."
puts $dbfile_ID [::msgcat::mc m90]
# Texte : "--- ducks_shot : Nombre de canards abattus."
puts $dbfile_ID [::msgcat::mc m91]
# Texte : "--- missed_shots : Nombre de tirs manqués (inclut les accidents de chasse)."
puts $dbfile_ID [::msgcat::mc m92]
# Texte : "--- empty_shots : Nombre de tentatives de tir sans aucune balle dans le chargeur."
puts $dbfile_ID [::msgcat::mc m93]
# Texte : "--- humans_shot : Nombre d'utilisateurs touchés par accident."
puts $dbfile_ID [::msgcat::mc m94]
# Texte : "--- wild_shots : Nombre de tirs effectués sans qu'il y ait de canard en vol."
puts $dbfile_ID [::msgcat::mc m95]
# Texte : "--- bullets_received : Nombre de fois la cible lors d'un accident de chasse."
puts $dbfile_ID [::msgcat::mc m96]
# Texte : "--- deflected_bullets : Nombre de tirs accidentels défléchis vers un autre utilisateur."
puts $dbfile_ID [::msgcat::mc m97]
# Texte : "--- deaths : Nombre de fois mort par balle."
puts $dbfile_ID [::msgcat::mc m98]
# Texte : "--- confiscated_weapons : Nombre d'armes confisquées."
puts $dbfile_ID [::msgcat::mc m99]
# Texte : "--- jammed_weapons : Nombre d'armes enrayées."
puts $dbfile_ID [::msgcat::mc m100]
# Texte : "--- best_time : Meilleur temps pour abattre un canard. (-1 = pas encore initialisé)"
puts $dbfile_ID [::msgcat::mc m101]
# Texte : "--- cumul_reflex_time : Temps de réflexe cumulé."
puts $dbfile_ID [::msgcat::mc m202]
# Texte : "--- nick : Nom du joueur avec la casse de caractères d'origine."
puts $dbfile_ID [::msgcat::mc m203]
# Texte : "--- items : Objets spéciaux possédés par le joueur."
puts $dbfile_ID [::msgcat::mc m203]
# Texte : "--- golden_ducks : Nombre de super-canards abattus."
puts $dbfile_ID [::msgcat::mc m272]
# Texte : "--- last_activity : Date à laquelle le joueur a été actif pour la dernière fois."
puts $dbfile_ID [::msgcat::mc m423]
puts $dbfile_ID "---"
puts $dbfile_ID [::tcl::dict::get $::DuckHunt::player_data]
close $dbfile_ID
return
}
###############################################################################
### Déchargement de la base de données de la mémoire après vérification qu'elle
### n'est plus en cours d'utilisation.
###############################################################################
proc ::DuckHunt::purge_db_from_memory {} {
incr ::DuckHunt::db_sessions -1
if {
(!$::DuckHunt::db_sessions)
&& ([::tcl::info::exists ::DuckHunt::player_data])
} then {
unset ::DuckHunt::player_data
}
}
###############################################################################
### Suivi des changements de nick effectués en présence de l'Eggdrop afin de
### tenir à jour le nom des joueurs dans la base de données.
###############################################################################
proc ::DuckHunt::nickchange_tracking {oldnick host hand chan newnick} {
if {
([channel get $chan DuckHunt])
&& ([set lower_oldnick [::tcl::string::tolower $oldnick]] ne [set lower_newnick [::tcl::string::tolower $newnick]])
} then {
set regexpable_prefix [regsub -all {\W} $::DuckHunt::anonym_prefix {\\&}]
if {
($::DuckHunt::anonym_prefix ne "")
&& (![regexp -nocase "^$regexpable_prefix\[0-9\]+$" $oldnick])
&& ([regexp -nocase "^$regexpable_prefix\[0-9\]+$" $newnick])
} then {
return
} else {
set old_hash [md5 "$chan,$lower_oldnick"]
set new_hash [md5 "$chan,$lower_newnick"]
::DuckHunt::read_database
if { [::tcl::dict::exists $::DuckHunt::player_data $chan] } {
# Si $newnick a des stats, on note le changement de nick mais on n'opère
# pas le transfert ou la fusion maintenant.
if { [::tcl::dict::exists $::DuckHunt::player_data $chan $lower_newnick] } {
# On crée un array de la forme pending_transfers($new_hash)={$oldnick $newnick}
# Le renommage ou la fusion ne seront opérés qu'au moment où joueur
# participera au jeu dans le but de réduire les risques de vols de
# scores.
# Si une entrée inverse existe déjà dans l'array, on la supprime et on
# n'en crée pas de nouvelle.
if {
([::tcl::info::exists ::DuckHunt::pending_transfers($old_hash)])
&& ([lindex [::tcl::string::tolower $::DuckHunt::pending_transfers($old_hash)] 0] eq $lower_newnick)
} then {
unset ::DuckHunt::pending_transfers($old_hash)
# Sinon, si deux entrées peuvent être factorisées, on le fait.
} elseif { $old_hash in [array names ::DuckHunt::pending_transfers] } {
set ::DuckHunt::pending_transfers($new_hash) $::DuckHunt::pending_transfers($old_hash)
unset ::DuckHunt::pending_transfers($old_hash)
if { $::DuckHunt::warn_on_rename } {
# Message : "\00314\[%s - surveillance\]\017 \002%s\002 (%s) s'est renommé en \002%s\002. \037Remarque\037 : des statistiques existent déjà à ce nom sur %s."
::DuckHunt::display_output loglev - - [::msgcat::mc m139 $::DuckHunt::scriptname $oldnick [getchanhost $newnick] $newnick $chan]
}
# Sinon on ajoute simplement une entrée.
} else {
set ::DuckHunt::pending_transfers($new_hash) [list $oldnick $newnick]
if { $::DuckHunt::warn_on_rename } {
# Message : "\00314\[%s - surveillance\]\017 \002%s\002 (%s) s'est renommé en \002%s\002. \037Remarque\037 : des statistiques existent déjà à ce nom sur %s."
::DuckHunt::display_output loglev - - [::msgcat::mc m139 $::DuckHunt::scriptname $oldnick [getchanhost $newnick] $newnick $chan]
}
}
# Si $newnick n'a pas de stats, que $oldnick en avait et qu'aucune
# entrée n'existe à ce nom dans pending_transfers, on ajoute une entrée.
} elseif {
([::tcl::dict::exists $::DuckHunt::player_data $chan $lower_oldnick])
&& !([::tcl::info::exists ::DuckHunt::pending_transfers($old_hash)])
} then {
set ::DuckHunt::pending_transfers($new_hash) [list $oldnick $newnick]
# Suppression d'une éventuelle entrée inverse dans pending_transfers.
} elseif {
([::tcl::info::exists ::DuckHunt::pending_transfers($old_hash)])
&& ([lindex [::tcl::string::tolower $::DuckHunt::pending_transfers($old_hash)] 0] eq $lower_newnick)
} then {
unset ::DuckHunt::pending_transfers($old_hash)
}
}
::DuckHunt::purge_db_from_memory
}
::DuckHunt::update_nickchange_tracking_db
}
}
###############################################################################
### Mise à jour de $::DuckHunt::pending_transfers si l'utilisateur quitte le
### chan.
###############################################################################
proc ::DuckHunt::update_nickchange_tracking {nick host hand chan msg} {
set hash [md5 "$chan,[::tcl::string::tolower $nick]"]
if { [::tcl::info::exists ::DuckHunt::pending_transfers($hash)] } {
unset ::DuckHunt::pending_transfers($hash)
}
::DuckHunt::update_nickchange_tracking_db
}
###############################################################################
### Mise à jour de la db contenant le suivi des changements de nick.
###############################################################################
proc ::DuckHunt::update_nickchange_tracking_db {} {
set pending_transfers_file_ID [open $::DuckHunt::pending_transfers_file w]
puts $pending_transfers_file_ID [array get ::DuckHunt::pending_transfers]
close $pending_transfers_file_ID
}
###############################################################################
### Vérification des renommages / fusions de profils en attente.
###############################################################################
proc ::DuckHunt::ckeck_for_pending_rename {chan newnick lower_newnick nick_hash} {
# Si le participant vient de changer de nick, on transfère ses statistiques.
if { [::tcl::info::exists ::DuckHunt::pending_transfers($nick_hash)] } {
set lower_oldnick [::tcl::string::tolower [set oldnick [lindex $::DuckHunt::pending_transfers($nick_hash) 0]]]
# Si $oldnick avait des stats, on effectue une fusion avec celles de
# $newnick.
if { [::tcl::dict::exists $::DuckHunt::player_data $chan $lower_oldnick] } {
foreach varname {gun jammed current_ammo_clip remaining_ammo_clips xp ducks_shot missed_shots empty_shots humans_shot wild_shots bullets_received deflected_bullets deaths confiscated_weapons jammed_weapons best_time cumul_reflex_time nick items golden_ducks_shot last_activity} {
lappend src_stats [::DuckHunt::get_data $lower_oldnick $chan $varname]
lappend dst_stats [::DuckHunt::get_data $lower_newnick $chan $varname]
}
# Si l'arme de newnick est confisquée mais pas celle de oldnick, alors les
# stats de oldnick ne seront pas conservées.
if {
($::DuckHunt::confiscation_enforcement_on_fusion)
&& ([::DuckHunt::get_data $lower_newnick $chan "gun"] < 1)
&& ([::DuckHunt::get_data $lower_oldnick $chan "gun"] == 1)
} then {
if { $::DuckHunt::warn_on_takeover } {
# Message : "\00314\[%s - surveillance\]\017 \002%s\002 (%s) s'est renommé en \002%s\002. Des statistiques existent pour les deux noms mais puisque %s est désarmé, %s devrait également l'être. Pour cette raison, les statistiques de %s n'ont pas été conservées : \{%s\}"
::DuckHunt::display_output loglev - - [::msgcat::mc m245 $::DuckHunt::scriptname $oldnick [getchanhost $newnick] $newnick $newnick $oldnick $oldnick $src_stats]
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan [unixtime] $oldnick - $newnick - "merge_stats_1" 0 [list $src_stats $dst_stats $dst_stats]
}
}
::tcl::dict::unset ::DuckHunt::player_data $chan $lower_oldnick
# Si aucun des deux joueurs n'a d'arme, on ne conserve que les stats de
# celui qui a le plus d'xp.
} elseif {
($::DuckHunt::confiscation_enforcement_on_fusion)
&& ([::DuckHunt::get_data $lower_newnick $chan "gun"] < 1)
&& ([::DuckHunt::get_data $lower_oldnick $chan "gun"] < 1)
} then {
if { [::DuckHunt::get_data $lower_newnick $chan "xp"] >= [::DuckHunt::get_data $lower_oldnick $chan "xp"] } {
if { $::DuckHunt::warn_on_takeover } {
# Message : "\00314\[%s - surveillance\]\017 \002%s\002 (%s) s'est renommé en \002%s\002. Des statistiques existent pour les deux noms mais puisque les deux joueurs sont désarmés, seules les statistiques du joueur ayant le plus d'xp seront conservées. Pour cette raison, les statistiques de %s n'ont pas été conservées : \{%s\}"
::DuckHunt::display_output loglev - - [::msgcat::mc m246 $::DuckHunt::scriptname $oldnick [getchanhost $newnick] $newnick $oldnick $src_stats]
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan [unixtime] $oldnick - $newnick - "merge_stats_2" 0 [list $src_stats $dst_stats $dst_stats]
}
}
::tcl::dict::unset ::DuckHunt::player_data $chan $lower_oldnick
} else {
if { $::DuckHunt::warn_on_takeover } {
# Message : "\00314\[%s - surveillance\]\017 \002%s\002 (%s) s'est renommé en \002%s\002. Des statistiques existent pour les deux noms mais puisque les deux joueurs sont désarmés, seules les statistiques du joueur ayant le plus d'xp seront conservées. Pour cette raison, les statistiques de %s n'ont pas été conservées : \{%s\}"
::DuckHunt::display_output loglev - - [::msgcat::mc m246 $::DuckHunt::scriptname $oldnick [getchanhost $newnick] $newnick $newnick $dst_stats]
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan [unixtime] $oldnick - $newnick - "merge_stats_2" 0 [list $src_stats $dst_stats $src_stats]
}
}
::tcl::dict::set ::DuckHunt::player_data $chan $lower_newnick [::tcl::dict::get $::DuckHunt::player_data $chan $lower_oldnick]
::tcl::dict::set ::DuckHunt::player_data $chan $lower_newnick "nick" $newnick
::tcl::dict::unset ::DuckHunt::player_data $chan $lower_oldnick
}
} else {
if { $::DuckHunt::warn_on_takeover } {
# Texte : "\00314\[%s - surveillance\]\017 \002%s\002 (%s) s'est renommé en \002%s\002. Des statistiques existent pour les deux noms et ont été fusionnées. Etat avant fusion : %s \{%s\} / %s \{%s\}. Ce message s'affiche afin de vous sensibiliser à la possibilité de l'appropriation illégitime de stats appartenant à autrui."
::DuckHunt::display_output loglev - - [::msgcat::mc m102 $::DuckHunt::scriptname $oldnick [getchanhost $newnick] $newnick $oldnick $src_stats $newnick $dst_stats]
}
::DuckHunt::merge_stats $chan $oldnick $lower_oldnick $newnick $lower_newnick $src_stats $dst_stats 0
}
::DuckHunt::recalculate_ammo_on_lvl_change $lower_newnick $chan
::DuckHunt::write_database
# Si $oldnick n'avait pas de stats, on signale la réattribution en PL.
} elseif { [::tcl::dict::exists $::DuckHunt::player_data $chan $lower_newnick] } {
foreach varname {gun jammed current_ammo_clip remaining_ammo_clips xp ducks_shot missed_shots empty_shots humans_shot wild_shots bullets_received deflected_bullets deaths confiscated_weapons jammed_weapons best_time cumul_reflex_time nick items golden_ducks_shot last_activity} {
lappend dst_stats [::DuckHunt::get_data $lower_newnick $chan $varname]
}
if { $::DuckHunt::warn_on_takeover } {
# Texte : "\00314\[%s - surveillance\]\017 \002%s\002 (%s) s'est renommé en \002%s\002. Des statistiques sont enregistrées au nom de %s, mais pas au nom de %s; veuillez vous assurer qu'il s'agit bien de la même personne. Etat actuel des stats : %s \{%s\}. Ce message s'affiche afin de vous sensibiliser à la possibilité de l'appropriation illégitime de stats appartenant à autrui."
::DuckHunt::display_output loglev - - [::msgcat::mc m127 $::DuckHunt::scriptname $oldnick [getchanhost $newnick] $newnick $newnick $oldnick $newnick $dst_stats]
if { $::DuckHunt::hunting_logs } {
::DuckHunt::add_to_log $chan [unixtime] $oldnick - $newnick - "merge_stats_3" 0 [list {} $dst_stats $dst_stats]
}
}
}
unset ::DuckHunt::pending_transfers($nick_hash)
::DuckHunt::update_nickchange_tracking_db
}
}
###############################################################################
### Fusionne les statistiques d'un joueur avec celles d'un autre.
###############################################################################
proc ::DuckHunt::merge_stats {chan src_nick lower_src_nick dst_nick lower_dst_nick args} {
foreach varname {xp ducks_shot golden_ducks_shot missed_shots empty_shots humans_shot wild_shots bullets_received deflected_bullets deaths confiscated_weapons jammed_weapons cumul_reflex_time} {
set $varname [expr {[::DuckHunt::get_data $lower_src_nick $chan $varname] + [::DuckHunt::get_data $lower_dst_nick $chan $varname]}]
}
set gun [expr {min([::DuckHunt::get_data $lower_src_nick $chan "gun"],[::DuckHunt::get_data $lower_dst_nick $chan "gun"])}]
set jammed [expr {max([::DuckHunt::get_data $lower_src_nick $chan "jammed"],[::DuckHunt::get_data $lower_dst_nick $chan "jammed"])}]
# On détermine le nombre de munitions utilisées par chacun depuis le dernier
# réapprovisionnement, en supposant que les armes étaient chargées au départ
# (impossible à vérifier).
# Le nombre total de munitions utilisées est ensuite déduit du nombre maximum
# de munitions du profil de destination.
lassign [::DuckHunt::get_level_and_grantings [::DuckHunt::get_data $lower_src_nick $chan "xp"]] {} {} {} {} {} {} src_ammos_per_clip src_ammo_clips
lassign [::DuckHunt::get_level_and_grantings [::DuckHunt::get_data $lower_dst_nick $chan "xp"]] {} {} {} {} {} {} dst_ammos_per_clip dst_ammo_clips
set merged_full_ammo [expr {($src_ammos_per_clip * $src_ammo_clips) + $src_ammos_per_clip + ($dst_ammos_per_clip * $dst_ammo_clips) + $dst_ammos_per_clip}]
set src_ammo_in_clips [expr {([::DuckHunt::get_data $lower_src_nick $chan "remaining_ammo_clips"] * $src_ammos_per_clip)}]
set src_ammo_in_gun [::DuckHunt::get_data $lower_src_nick $chan "current_ammo_clip"]
set dst_ammo_in_clips [expr {([::DuckHunt::get_data $lower_dst_nick $chan "remaining_ammo_clips"] * $dst_ammos_per_clip)}]
set dst_ammo_in_gun [::DuckHunt::get_data $lower_dst_nick $chan "current_ammo_clip"]
set used_ammo [expr {$merged_full_ammo - $src_ammo_in_clips - $src_ammo_in_gun - $dst_ammo_in_clips - $dst_ammo_in_gun}]
lassign [::DuckHunt::get_level_and_grantings $xp] {} {} {} {} {} {} resulting_ammos_per_clip resulting_ammo_clips
set current_ammo_clip [expr {$resulting_ammos_per_clip - ($used_ammo % $resulting_ammos_per_clip)}]
set remaining_ammo_clips [expr {$resulting_ammo_clips - int($used_ammo / [format "%.1f" $resulting_ammos_per_clip])}]
if { $remaining_ammo_clips < 0 } {
set remaining_ammo_clips 0
set current_ammo_clip 0
}
if { [::DuckHunt::get_data $lower_src_nick $chan "best_time"] == -1 } {
set best_time [::DuckHunt::get_data $lower_dst_nick $chan "best_time"]
} elseif { [::DuckHunt::get_data $lower_dst_nick $chan "best_time"] == -1 } {
set best_time [::DuckHunt::get_data $lower_src_nick $chan "best_time"]
} else {
set best_time [expr {min([::DuckHunt::get_data $lower_src_nick $chan "best_time"],[::DuckHunt::get_data $lower_dst_nick $chan "best_time"])}]
}
set nick [::DuckHunt::get_data $lower_dst_nick $chan "nick"]
set items [lsort -unique -index 1 [lsort -dictionary -index 0 [concat [::DuckHunt::get_data $lower_src_nick $chan "items"] [::DuckHunt::get_data $lower_dst_nick $chan "items"]]]]
set last_activity [expr {max([::DuckHunt::get_data $lower_src_nick $chan "last_activity"],[::DuckHunt::get_data $lower_dst_nick $chan "last_activity"])}]
::tcl::dict::set ::DuckHunt::player_data $chan $lower_dst_nick [::tcl::dict::create "gun" $gun "jammed" $jammed "current_ammo_clip" $current_ammo_clip "remaining_ammo_clips" $remaining_ammo_clips "xp" $xp "ducks_shot" $ducks_shot "missed_shots" $missed_shots "empty_shots" $empty_shots "humans_shot" $humans_shot "wild_shots" $wild_shots "bullets_received" $bullets_received "deflected_bullets" $deflected_bullets "deaths" $deaths "confiscated_weapons" $confiscated_weapons "jammed_weapons" $jammed_weapons "best_time" $best_time "cumul_reflex_time" $cumul_reflex_time "nick" $nick "items" $items "golden_ducks_shot" $golden_ducks_shot "last_activity" $last_activity]
::tcl::dict::unset ::DuckHunt::player_data $chan $lower_src_nick
if { $::DuckHunt::hunting_logs } {
lassign $args src_stats dst_stats is_manual
if { $is_manual } {
::DuckHunt::add_to_log $chan [unixtime] $src_nick - $dst_nick - "merge_stats_5" 0 [list $src_stats $dst_stats [list $gun $jammed $current_ammo_clip $remaining_ammo_clips $xp $ducks_shot $missed_shots $empty_shots $humans_shot $wild_shots $bullets_received $deflected_bullets $deaths $confiscated_weapons $jammed_weapons $best_time $cumul_reflex_time $items $golden_ducks_shot $last_activity]]
} else {
::DuckHunt::add_to_log $chan [unixtime] $src_nick - $dst_nick - "merge_stats_4" 0 [list $src_stats $dst_stats [list $gun $jammed $current_ammo_clip $remaining_ammo_clips $xp $ducks_shot $missed_shots $empty_shots $humans_shot $wild_shots $bullets_received $deflected_bullets $deaths $confiscated_weapons $jammed_weapons $best_time $cumul_reflex_time $items $golden_ducks_shot $last_activity]]
}
}
}
###############################################################################
### Affichage d'un texte, filtrage des styles si nécessaire.
### * queue peut valoir help, quick, now, serv, dcc, log ou loglev
### * method peut valoir PRIVMSG ou NOTICE et sera ignoré si queue vaut dcc ou
### loglev
### * target peut être un nick, un chan ou un idx, et sera ignoré si queue vaut
### loglev
###############################################################################
proc ::DuckHunt::display_output {queue method target text} {
if {
($::DuckHunt::monochrome)
|| (!([::tcl::string::first "#" $target])
&& ([::tcl::string::match *c* [lindex [split [getchanmode $target]] 0]]))
|| (($queue eq "dcc")
&& (![matchattr [idx2hand $target] h]))
} then {
# Remarque : l'aller-retour d'encodage permet de contourner un bug d'Eggdrop
# qui corromp le charset dans certaines conditions lors de l'utilisation de
# regsub sur une chaîne de caractères.
regsub -all "\017" [stripcodes abcgru [encoding convertto utf-8 [encoding convertfrom utf-8 $text]]] "" text
}
switch -- $queue {
help - quick - now - serv {
foreach line [::DuckHunt::split_line $text $::DuckHunt::max_line_length] {
put$queue "$method $target :$line"
}
}
dcc {
foreach line [::DuckHunt::split_line $text $::DuckHunt::max_line_length] {
putdcc $target $line
}
}
loglev {
foreach line [::DuckHunt::split_line $text $::DuckHunt::max_line_length] {
putloglev o * $line
}
}
log {
foreach line [::DuckHunt::split_line $text $::DuckHunt::max_line_length] {
putlog $line
}
}
}
}
###############################################################################
### Découpage d'une ligne trop longue en plusieurs lignes en essayant de couper
### sur les espaces autant que possible. Si l'espace le plus proche est à plus
### de 50% de la fin de la ligne, on s'autorise à couper au milieu d'un mot.
### Les \n provoquent un retour forcé à la ligne et les styles (couleurs, gras,
### ...) sont préservés d'une ligne à l'autre.
### $limit doit être >= 9
### Remerciements à ealexp pour la fonction de préservation des styles.
###############################################################################
proc ::DuckHunt::split_line {data limit} {
incr limit -1
if { [::tcl::string::length $data] <= $limit } {
return [expr {$data eq "" ? [list ""] : [split $data "\n"]}]
} else {
# Note : si l'espace le plus proche est situé à plus de 50% de la fin du
# fragment, on n'hésite pas à couper au milieu d'un mot.
set middle_pos [expr round($limit / 2.0)]
set output ""
while {1} {
if {
([set cut_index [::tcl::string::first "\n" $data]] != -1)
&& ($cut_index <= $limit)
} then {
# On ne fait rien de plus, on vient de définir $cut_index.
} elseif {
([set cut_index [::tcl::string::last " " $data [expr {$limit + 1}]]] == -1)
|| ($cut_index < $middle_pos)
} then {
set new_cut_index -1
# On vérifie qu'on ne va pas couper dans la définition d'une couleur.
for { set i 0 } { $i < 6 } { incr i } {
if {
([::tcl::string::index $data [set test_cut_index [expr {$limit - $i}]]] eq "\003")
&& ([regexp {^\003([0-9]{1,2}(,[0-9]{1,2})?)} [::tcl::string::range $data $test_cut_index end]])
} then {
set new_cut_index [expr {$test_cut_index - 1}]
}
}
set cut_index [expr {($new_cut_index == -1) ? ($limit) : ($new_cut_index)}]
}
set new_part [::tcl::string::range $data 0 $cut_index]
set data [::tcl::string::range $data $cut_index+1 end]
if { [::tcl::string::trim [::tcl::string::map [list \002 {} \037 {} \026 {} \017 {}] [regsub -all {\003([0-9]{0,2}(,[0-9]{0,2})?)?} $new_part {}]]] ne "" } {
lappend output [::tcl::string::trimright $new_part]
}
# Si, quand on enlève les espaces et les codes de formatage, il ne reste
# plus rien, pas la peine de continuer.
if { [::tcl::string::trim [::tcl::string::map [list \002 {} \037 {} \026 {} \017 {}] [regsub -all {\003([0-9]{0,2}(,[0-9]{0,2})?)?} $data {}]]] eq "" } {
break
}
set taglist [regexp -all -inline {\002|\003(?:[0-9]{0,2}(?:,[0-9]{0,2})?)?|\037|\026|\017} $new_part]
# Etat des tags "au repos"; les -1 signifient que la couleur est celle par
# défaut.
set bold 0 ; set underline 0 ; set italic 0 ; set foreground_color "-1" ; set background_color "-1"
foreach tag $taglist {
if {$tag eq ""} {
continue
}
switch -- $tag {
"\002" {
if { !$bold } {
set bold 1
} else {
set bold 0
}
}
"\037" {
if { !$underline } {
set underline 1
} else {
set underline 0
}
}
"\026" {
if { !$italic } {
set italic 1
} else {
set italic 0
}
}
"\017" {
set bold 0
set underline 0
set italic 0
set foreground_color -1
set background_color -1
}
default {
lassign [split [regsub {\003([0-9]{0,2}(,[0-9]{0,2})?)?} $tag {\1}] ","] foreground_color background_color
if {$foreground_color eq ""} {
set foreground_color -1 ; set background_color -1
} elseif {
($foreground_color < 10)
&& ([::tcl::string::index $foreground_color 0] ne "0")
} then {
set foreground_color 0$foreground_color
}
if {$background_color eq ""} {
set background_color -1
} elseif {
($background_color < 10)
&& ([::tcl::string::index $background_color 0] ne "0")
} then {
set background_color 0$background_color
}
}
}
}
set line_start ""
if { $bold } {
append line_start \002
}
if { $underline } {
append line_start \037
}
if { $italic } {
append line_start \026
}
if {
($foreground_color != -1)
&& ($background_color == -1)
} then {
append line_start \003$foreground_color
}
if {
($foreground_color != -1)
&& ($background_color != -1)
} then {
append line_start \003$foreground_color,$background_color
}
set data ${line_start}$data
}
return $output
}
}
###############################################################################
### Contrôle du flood.
### - focus peut valoir "chan" ou "nick" et différenciera un contrôle de flood
### collectif où les commandes seront bloquées pour tout le monde, d'un
### contrôle individuel où les commandes seront bloquées pour un seul individu.
### - command peut valoir "*" ou le nom d'une commande et différenciera un
### contrôle par commande ou toutes commandes du script confondues.
### - limit est exprimé sous la forme "xx:yy", où xx = nombre maximum de
### requêtes et yy = durée d'une instance.
###############################################################################
proc ::DuckHunt::antiflood {nick chan focus command limit} {
lassign [split $limit ":"] max_instances instance_length
if { $focus eq "chan" } {
set hash [md5 "$chan,$command"]
} else {
set hash [md5 "$nick,$chan,$command"]
}
# L'antiflood est dans un statut neutre, on l'initialise.
if { ![::tcl::info::exists ::DuckHunt::instance($hash)] } {
set ::DuckHunt::instance($hash) 0
set ::DuckHunt::antiflood_msg($hash) 0
}
if { $::DuckHunt::instance($hash) >= $max_instances } {
if { $::DuckHunt::preferred_display_mode == 1 } {
set output_method "PRIVMSG"
set output_target $chan
} else {
set output_method "NOTICE"
set output_target $nick
}
if { !$::DuckHunt::antiflood_msg($hash) } {
set ::DuckHunt::antiflood_msg($hash) 2
if { $command eq "*" } {
if { $focus eq "chan" } {
# Message : "\00304:::\003 \00314Contrôle de flood activé pour toutes les commandes du script %s : pas plus de %s %s toutes les %s %s.\003"
# Textes : "requête" "requêtes" "seconde" "secondes"
::DuckHunt::display_output help PRIVMSG $chan [::msgcat::mc m103 $::DuckHunt::scriptname $max_instances [::DuckHunt::plural $max_instances [::msgcat::mc m104] [::msgcat::mc m105]] $instance_length [::DuckHunt::plural $instance_length [::msgcat::mc m106] [::msgcat::mc m107]]]
} else {
# Message : "\00304:::\003 \00314Contrôle de flood activé pour %s sur toutes les commandes du script %s : pas plus de %s %s toutes les %s %s.\003"
# Textes : "requête" "requêtes" "seconde" "secondes"
::DuckHunt::display_output help PRIVMSG $chan [::msgcat::mc m421 $nick $::DuckHunt::scriptname $max_instances [::DuckHunt::plural $max_instances [::msgcat::mc m104] [::msgcat::mc m105]] $instance_length [::DuckHunt::plural $instance_length [::msgcat::mc m106] [::msgcat::mc m107]]]
}
} else {
if { $focus eq "chan" } {
# Message : "\00304:::\003 \00314Contrôle de flood activé pour la commande \"%s\" : pas plus de %s %s toutes les %s %s.\003"
# Textes : "requête" "requêtes" "seconde" "secondes"
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m108 $command $max_instances [::DuckHunt::plural $max_instances [::msgcat::mc m104] [::msgcat::mc m105]] $instance_length [::DuckHunt::plural $instance_length [::msgcat::mc m106] [::msgcat::mc m107]]]
} else {
# Message : "\00304:::\003 \00314Contrôle de flood activé pour %s sur la commande \"%s\" : pas plus de %s %s toutes les %s %s.\003"
# Textes : "requête" "requêtes" "seconde" "secondes"
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m273 $nick $command $max_instances [::DuckHunt::plural $max_instances [::msgcat::mc m104] [::msgcat::mc m105]] $instance_length [::DuckHunt::plural $instance_length [::msgcat::mc m106] [::msgcat::mc m107]]]
}
}
if { [set msgresettimer [::DuckHunt::utimerexists "::DuckHunt::antiflood_msg_reset $hash"]] ne ""} {
killutimer $msgresettimer
}
utimer $::DuckHunt::antiflood_msg_interval [list ::DuckHunt::antiflood_msg_reset $hash]
} elseif { $::DuckHunt::antiflood_msg($hash) == 1 } {
set ::DuckHunt::antiflood_msg($hash) 2
if { $command eq "*" } {
# Message : "\00304:::\003 \00314Le contrôle de flood est toujours actif, merci de patienter.\003"
::DuckHunt::display_output help PRIVMSG $chan [::msgcat::mc m109]
} else {
# Message : "\00304:::\003 \00314Le contrôle de flood est toujours actif, merci de patienter.\003"
::DuckHunt::display_output help $output_method $output_target [::msgcat::mc m109]
}
if { [set msgresettimer [::DuckHunt::utimerexists "::DuckHunt::antiflood_msg_reset $hash"]] ne ""} {
killutimer $msgresettimer
}
utimer $::DuckHunt::antiflood_msg_interval [list ::DuckHunt::antiflood_msg_reset $hash]
}
return "1"
} else {
incr ::DuckHunt::instance($hash) 1
utimer $instance_length [list ::DuckHunt::antiflood_close_instance $hash]
return "0"
}
}
proc ::DuckHunt::antiflood_close_instance {hash} {
incr ::DuckHunt::instance($hash) -1
# si le nombre d'instances retombe à 0, on efface les variables instance et
# antiflood_msg afin de ne pas encombrer la mémoire inutilement
if { $::DuckHunt::instance($hash) == 0 } {
unset ::DuckHunt::instance($hash)
unset ::DuckHunt::antiflood_msg($hash)
# le nombre d'instances est retombé en dessous du seuil critique, on
# réinitialise antiflood_msg
} else {
set ::DuckHunt::antiflood_msg($hash) 0
if { [set msgresettimer [::DuckHunt::utimerexists "::DuckHunt::antiflood_msg_reset $hash"]] ne ""} {
killutimer $msgresettimer
}
}
return
}
proc ::DuckHunt::antiflood_msg_reset {hash} {
set ::DuckHunt::antiflood_msg($hash) 1
return
}
###############################################################################
### Formatage d'un nombre décimal.
### La précision détermine le nombre de décimales à conserver.
### Les 0 se trouvant à la fin seront supprimés.
###############################################################################
proc ::DuckHunt::format_floating_point_value {value precision} {
return [::tcl::string::trimright [::tcl::string::trimright [format "%.${precision}f" $value] 0] "."]
}
###############################################################################
### Test de l'existence d'un utimer, renvoi de son ID
###############################################################################
proc ::DuckHunt::utimerexists {command} {
foreach utimer_ [utimers] {
if { ![::tcl::string::compare $command [lindex $utimer_ 1]] } {
return [lindex $utimer_ 2]
}
}
}
###############################################################################
### Test de l'existence d'un timer, renvoi de son ID
###############################################################################
proc ::DuckHunt::timerexists {command} {
foreach timer_ [timers] {
if { ![::tcl::string::compare $command [lindex $timer_ 1]] } {
return [lindex $timer_ 2]
}
}
}
###############################################################################
### Transforme un temps en millisecondes en temps lisible avec une résolution
### dynamique.
###############################################################################
proc ::DuckHunt::adapt_time_resolution {duration short} {
set milliseconds [::tcl::string::range $duration end-2 end]
set duration [::tcl::string::range $duration 0 end-3]
if { $duration eq "" } {
set duration 0
}
set days [expr {abs($duration / 86400)}]
set hours [expr {abs(($duration % 86400) / 3600)}]
set minutes [expr {abs(($duration % 3600) / 60)}]
set seconds [expr {$duration % 60}]
set valid_units 0
set counter 1
set output {}
foreach unit [list $days $hours $minutes $seconds] {
if {
($unit > 0)
|| ($counter == 4)
} then {
switch -- $counter {
1 {
if { !$short } {
# Textes : "jour" "jours"
lappend output "$unit [::DuckHunt::plural $unit [::msgcat::mc m110] [::msgcat::mc m111]]"
} else {
# Texte : "j"
lappend output "${unit}[::msgcat::mc m112]"
}
}
2 {
if { !$short } {
# Textes : "heure" "heures"
lappend output "$unit [::DuckHunt::plural $unit [::msgcat::mc m113] [::msgcat::mc m114]]"
} else {
# Texte : "h"
lappend output "${unit}[::msgcat::mc m115]"
}
}
3 {
if { !$short } {
# Textes : "minute" "minutes"
lappend output "$unit [::DuckHunt::plural $unit [::msgcat::mc m116] [::msgcat::mc m117]]"
} else {
# Texte : "mn"
lappend output "${unit}[::msgcat::mc m118]"
}
}
4 {
if { [set milliseconds [::tcl::string::trimright $milliseconds 0]] == "" } {
set show_ms 0
} else {
set show_ms 1
}
if { $show_ms } {
if { !$short } {
# Textes : "seconde" "secondes"
lappend output "${unit}.$milliseconds [::DuckHunt::plural "${unit}$milliseconds" [::msgcat::mc m106] [::msgcat::mc m107]]"
} else {
# Texte : "s"
lappend output "${unit}.${milliseconds}[::msgcat::mc m119]"
}
} else {
if { !$short } {
# Textes : "seconde" "secondes"
lappend output "$unit [::DuckHunt::plural $unit [::msgcat::mc m106] [::msgcat::mc m107]]"
} else {
# Texte : "s"
lappend output "$unit[::msgcat::mc m119]"
}
}
}
}
incr valid_units
}
incr counter
}
if { !$short } {
if { $valid_units > 1 } {
# Texte "et"
set output [linsert $output end-1 [::msgcat::mc m120]]
}
return [join $output]
} else {
return [join $output ""]
}
}
###############################################################################
### Correction de la casse des caractères du nom d'un chan.
###############################################################################
proc ::DuckHunt::fix_chan_case {chan} {
if { [validchan $chan] } {
return [lindex [set chanlist [channels]] [lsearch -nocase -exact $chanlist $chan]]
} else {
return $chan
}
}
###############################################################################
### Retourne une valeur en rouge si elle est <= 0
###############################################################################
proc ::DuckHunt::colorize_value {value} {
if { $value <= 0 } {
return "\00304$value\003"
} else {
return $value
}
}
###############################################################################
### Accorde au singulier ou au pluriel.
###############################################################################
proc ::DuckHunt::plural {value singular plural} {
if {
($value >= 2)
|| ($value <= -2)
} then {
return $plural
} else {
return $singular
}
}
###############################################################################
### Mélange aléatoirement les éléments d'une liste.
###############################################################################
proc ::DuckHunt::randomize_list {data} {
set list_length [llength $data]
for { set counter 1 } { $counter <= $list_length } { incr counter } {
set index [rand [expr {$list_length - $counter + 1}]]
lappend randomized_list [lindex $data $index]
set data [lreplace $data $index $index]
}
return $randomized_list
}
###############################################################################
### Backup quotidien de la base de données.
###############################################################################
proc ::DuckHunt::backup_db {min hour day month year} {
# Message : "\00314\[%s\]\003 Sauvegarde de la base de données..."
::DuckHunt::display_output loglev - - [::msgcat::mc m121 $::DuckHunt::scriptname]
if { [file exists $::DuckHunt::db_file] } {
file copy -force -- $::DuckHunt::db_file "${::DuckHunt::db_file}.bak"
}
}
###############################################################################
### Post-initialisation.
###############################################################################
# Relecture de la base de données de suivi de changements de nick.
if {
([file exists $::DuckHunt::pending_transfers_file])
&& ([file mtime $::DuckHunt::pending_transfers_file] + $::DuckHunt::pending_transfers_file_max_age > [unixtime])
} then {
set ::DuckHunt::pending_transfers_file_ID [open $::DuckHunt::pending_transfers_file r]
array set ::DuckHunt::pending_transfers [gets $::DuckHunt::pending_transfers_file_ID]
close $::DuckHunt::pending_transfers_file_ID
unset ::DuckHunt::pending_transfers_file_ID
}
if { $::DuckHunt::method == 2 } {
set ::DuckHunt::binds_tables {}
utimer $::DuckHunt::post_init_delay ::DuckHunt::post_init
} elseif { $::DuckHunt::shop_enabled } {
bind time - "* * * * *" ::DuckHunt::check_for_expired_pieces_of_bread
}
proc ::DuckHunt::post_init {} {
::DuckHunt::plan_out_flights
if { $::DuckHunt::shop_enabled } {
bind time - "* * * * *" ::DuckHunt::check_for_expired_pieces_of_bread
}
set ::DuckHunt::post_init_done 1
}
###############################################################################
### Binds
###############################################################################
bind evnt - prerehash ::DuckHunt::uninstall
if { $::DuckHunt::method == 1 } {
bind time - "* * * * *" ::DuckHunt::check_bushes_for_duck
} else {
bind time - "00 00 * * *" ::DuckHunt::plan_out_flights
bind msg $::DuckHunt::planning_auth $::DuckHunt::planning_cmd ::DuckHunt::show_planning
bind msg $::DuckHunt::replanning_auth $::DuckHunt::replanning_cmd ::DuckHunt::replan_flights
}
if {
($::DuckHunt::gun_hand_back_mode == 1)
&& (($::DuckHunt::gun_confiscation_when_shooting_someone)
|| ($::DuckHunt::gun_confiscation_on_wild_fire))
} then {
bind time - "[lindex [split $::DuckHunt::auto_gun_hand_back_time ":"] 1] [lindex [split $::DuckHunt::auto_gun_hand_back_time ":"] 0] * * *" ::DuckHunt::hand_back_weapons
}
bind time - "[lindex [split $::DuckHunt::auto_refill_ammo_time ":"] 1] [lindex [split $::DuckHunt::auto_refill_ammo_time ":"] 0] * * *" ::DuckHunt::refill_ammo
bind time - "[lindex [split $::DuckHunt::backup_time ":"] 1] [lindex [split $::DuckHunt::backup_time ":"] 0] * * *" ::DuckHunt::backup_db
bind pub $::DuckHunt::shooting_auth $::DuckHunt::shooting_cmd ::DuckHunt::shoot
bind pub $::DuckHunt::reload_auth $::DuckHunt::reload_cmd ::DuckHunt::reload_gun
bind pub $::DuckHunt::lastduck_pub_auth $::DuckHunt::lastduck_pub_cmd ::DuckHunt::pub_show_last_duck
bind msg $::DuckHunt::lastduck_msg_auth $::DuckHunt::lastduck_msg_cmd ::DuckHunt::msg_show_last_duck
bind pub $::DuckHunt::stat_auth $::DuckHunt::stat_cmd ::DuckHunt::display_stats
if { $::DuckHunt::shop_enabled } {
bind pub $::DuckHunt::shop_auth $::DuckHunt::shop_cmd ::DuckHunt::shop
}
bind pub $::DuckHunt::unarm_auth $::DuckHunt::unarm_cmd ::DuckHunt::unarm
bind pub $::DuckHunt::rearm_auth $::DuckHunt::rearm_cmd ::DuckHunt::rearm
bind msg $::DuckHunt::findplayer_auth $::DuckHunt::findplayer_cmd ::DuckHunt::findplayer
bind msg $::DuckHunt::fusion_auth $::DuckHunt::fusion_cmd ::DuckHunt::fusion
bind msg $::DuckHunt::rename_auth $::DuckHunt::rename_cmd ::DuckHunt::rename_player
bind msg $::DuckHunt::delete_auth $::DuckHunt::delete_cmd ::DuckHunt::delete_player
bind msg $::DuckHunt::launch_auth $::DuckHunt::launch_cmd ::DuckHunt::launch
bind msg $::DuckHunt::export_auth $::DuckHunt::export_cmd ::DuckHunt::export_players_table
bind nick -|- * ::DuckHunt::nickchange_tracking
bind part -|- * ::DuckHunt::update_nickchange_tracking
bind sign -|- * ::DuckHunt::update_nickchange_tracking
# Message : "%s v%s (©2015-2016 Menz Agitat) a été chargé."
namespace eval ::DuckHunt {
putlog [::msgcat::mc m1 $::DuckHunt::scriptname $::DuckHunt::version]
}