# OpenVerse Avatar Functions Module
# 
# This module includes all the functions related
# to avatars and avatar movement.
#
# Module Name		- Avatar Functions
# Current Maintainter 	- Cruise <cruise@openverse.org>
# Sourced By		- InitMainWindow
#
# Copyright (C) 1999 David Gale <cruise@openverse.org>
# For more information visit http://OpenVerse.org/
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
# USA.

# Additional Notes
#
# Performance enhancements with string comparisons.
# Performance using curleybraces around functions

# CRUISE - 11/08/2001 - Will only call itself if the avatar has multiple
#			frames and the animate option is set.
#
proc AnimateAvatar {} {
	global MV

	if {$MV(anim.current) == -1} {
		ChangeAvatar $MV(anim.0)
		set MV(anim.stop) 1
		set MV(anim.running) 0
		return
	}
	set delay 0
	if {$MV(anim.current) == 1 && !$MV(anim.new)} {
		set delay $MV(anim.final_delay)
	}
	if {$MV(anim.running) && $MV(anim.new)} {
		ChangeAvatar "$MV(anim.0)"
		set MV(anim.new) 0
		incr MV(anim.current)
		if {$MV(anim.current) > $MV(anim.frames)} {set MV(anim.current) 1}
		return
	}
	set MV(anim.new) 0
	if $MV(anim.stop) {
		ChangeAvatar "$MV(anim.0)"
		set MV(anim.running) 0
		return
	}
	ChangeAvatar "$MV(anim.[expr $MV(anim.current) -1])"
	set MV(anim.running) 1
	incr MV(anim.current)
	if {$MV(anim.current) > $MV(anim.frames)} {
		set MV(anim.current) 1
	}
	set do_delay [expr $MV(anim.delay) + $delay]
	if {$MV(anim.frames) > 1 && $MV(anim.animated)} {
		after $do_delay AnimateAvatar
	}
}

proc RaiseAvatar {who} {
	global MV errorInfo

	# Performance Enchancement DRG
	if {![string compare $who $MV(nick)]} {
		set image $MV(img)
	} else {
		if {[catch {set image $MV($who.icon)}]} {
			DebugIt "ERROR, Unable to raise $who (RaiseAvatar) Please report this error to cruise@openverse.org" other
			DebugIt "$errorInfo" other
			DebugIt "ERROR, Unable to raise $who (RaiseAvatar) Please report this error to cruise@openverse.org" other
			return
		}
	}
	.top.c raise $image
	if $MV(names) {
		KillName $who
		ShowName $who
	}
}

proc AnimateMe {what which} {
	global MV

	
	switch $which {
		1 {
			set file "$MV(avatar_files.$what)"
		}
		2 {
			set file "$MV(avatar_setup.$what)"
		}
		3 {
			if {![file exists "$MV(anims)/$what"]} {return}
			set file "$what"
		}
		default {
			set file "default.av"
		}
	}

	if {[winfo exists .setup.na.l.b.avatars.entry]} {
		.setup.na.l.b.avatars.entry delete 0 end
		.setup.na.l.b.avatars.entry insert end "$file"
	}
	catch {source "$MV(anims)/$file"}
	AnimateAvatar
}

proc ShowNames {} {
	global MV

	if $MV(names) {
		ShowName $MV(nick)
		#
		# Process Plugins!
		#
		foreach plugin $MV(plugin.traps.ShowNames) {
			if {![$MV(plugin.traps.ShowNames.$plugin) $MV(nick)]} {return}
		}
		foreach who $MV(people) {
			ShowName $who
			#
			# Process Plugins!
			#
			foreach plugin $MV(plugin.traps.ShowNames) {
				if {![$MV(plugin.traps.ShowNames.$plugin) $who]} {return}
			}
		}
	} else {
		KillName $MV(nick)
		#
		# Process Plugins!
		#
		foreach plugin $MV(plugin.traps.KillNames) {
			if {![$MV(plugin.traps.KillNames.$plugin) $MV(nick)]} {return}
		}
		foreach who $MV(people) {
			KillName $who
			#
			# Process Plugins!
			#
			foreach plugin $MV(plugin.traps.KillNames) {
				if {![$MV(plugin.traps.KillNames.$plugin) $who]} {return}
			}
		}
	}
}

proc KillName {who} {
	global MV

	catch {
		.top.c delete $MV($who.nametext)
		.top.c delete $MV($who.nameplate)
	}
}

#
# This is the function which displays the name on the screen.
# For the name plate.
#
# CRUISE - 10/24/2001 - took color codes out of name tags.
#
proc ShowName {who} {
	global MV

	# Performance Enhancement DRG
	if {![string compare $who $MV(nick)]} {
		set MV($MV(nick).x) $MV(x)
		set MV($MV(nick).y) $MV(y)
		set MV($MV(nick).name_x_offset) $MV(anim.x_off)
		set MV($MV(nick).name_y_offset) $MV(anim.y_off)
	}


	#
	# Replace underscores with spaces
	#
	set who_words [split $who "_"]
	set who_text [join $who_words " "]
	#
	# Strip the color codes
	#
	set who_text [StripColorCodes $who_text]
	set width [expr [string length $who_text] * 8]

	if $MV($who.moving) {
		set x $MV($who.current_x)
		set y $MV($who.current_y)
	} else {
		set x $MV($who.x)
		set y $MV($who.y)
	}

	#
	# Set so that a plugin can access this information.
	#
	set MV($who.name_tl_x) [expr ($x + $MV($who.name_x_offset)) - ($width / 2)]
	set MV($who.name_tl_y) [expr ($y + $MV($who.name_y_offset)) - 8]
	set MV($who.name_br_x) [expr ($x + $MV($who.name_x_offset)) + ($width / 2)]
	set MV($who.name_br_y) [expr ($y + $MV($who.name_y_offset)) + 8]
	set MV($who.nameplate) [.top.c create rectangle \
		[expr ($x + $MV($who.name_x_offset)) - ($width / 2)] \
		[expr ($y + $MV($who.name_y_offset)) - 8] \
		[expr ($x + $MV($who.name_x_offset)) + ($width / 2)] \
		[expr ($y + $MV($who.name_y_offset)) + 8] -fill black]
	set MV($who.nametext) [.top.c create text \
		[expr ($x + $MV($who.name_x_offset))] \
		[expr ($y + $MV($who.name_y_offset))] \
		-text $who_text -fill white]

	update idletasks
	#
	# Process Plugins!
	#
	foreach plugin $MV(plugin.traps.ShowName) {
		if {![$MV(plugin.traps.ShowName.$plugin) $who]} {return}
	}
}

proc ChangeAvatar {what} {
	global MV

	if {![file exists "$MV(images)/$what"]} {
		image create photo OpenVerse_Image_pic -file "$MV(images)/default.gif"
		return
	}
	#
	# Do not allow image files with spaces in their names.
	# Cruise - 10/18/2001
	#
	if {[string first " " "$what"] != -1} {
		ThrowError "[Trns space_in_filename] (IMAGE NAME IS $what)"
		image create photo OpenVerse_Image_pic -file "$MV(images)/default.gif"
		return
	}
	image create photo OpenVerse_Image_pic -file "$MV(images)/$what"
	set MV(avatar) $what
	SendToServer "AVATAR $what $MV(anim.x_off) $MV(anim.y_off) [file size "$MV(images)/$what"] $MV(anim.baloon_x) $MV(anim.baloon_y)"
	if $MV(names) {
		KillName $MV(nick)
		ShowName $MV(nick)
	}

	#
	# Process Plugins!
	#
	foreach plugin $MV(plugin.traps.ChangeAvatar) {
 		if {![$MV(plugin.traps.ChangeAvatar.$plugin) $what]} {return}
	}
}

proc DoAvatars {} {
	global MV

	set anims 0
	set depth 0
	set count 1

	destroy $MV(avatar_menu).m

	if $MV(use_windowmanager_colors) {
		menu $MV(avatar_menu).m
	} else {
		menu $MV(avatar_menu).m -bg $MV(colors.av.bg) \
			-fg $MV(colors.av.fg) -activeforeground $MV(colors.av.afg) \
			-activebackground $MV(colors.av.abg)
	}

	set MV(avatar_files.0) "default.av"
	foreach file [List_Avatars] {
		if !$depth {
			incr depth
			$MV(avatar_menu).m add cascade -menu \
				$MV(avatar_menu).m.m$depth -label "[Trns avatars] $depth"
			if $MV(use_windowmanager_colors) {
				menu $MV(avatar_menu).m.m$depth 
			} else {
				menu $MV(avatar_menu).m.m$depth -bg $MV(colors.av.bg) \
					-fg $MV(colors.av.fg) -activeforeground $MV(colors.av.afg) \
					-activebackground $MV(colors.av.abg)
			}

		}
		set MV(avatar_files.$count) "[file tail $file]"
		if {[file tail $file] == "default.av"} {
			set MV(default_avatar_number) $count
		}
		$MV(avatar_menu).m.m$depth add command -label [file tail $file] \
			-command "AnimateMe $count 1"
		incr anims
		incr count
		if {$anims > 19} {
			incr depth
			$MV(avatar_menu).m add cascade -menu \
				$MV(avatar_menu).m.m$depth -label "[Trns avatars] $depth"
			if $MV(use_windowmanager_colors) {
				menu $MV(avatar_menu).m.m$depth
			} else {
				menu $MV(avatar_menu).m.m$depth -bg $MV(colors.av.bg) \
					-fg $MV(colors.av.fg) -activeforeground $MV(colors.av.afg) \
					-activebackground $MV(colors.av.abg)
			}
			set anims 0
		}
	}
}

proc MoveTo {x y pushed} {
	global MV
	
	set ret 0
	if !$pushed {
		#
		# Process Plugins!
		#
		foreach plugin $MV(plugin.traps.MoveTo.Pre) {
			if {![$MV(plugin.traps.MoveTo.Pre.$plugin) $x $y]} {return}
		}
		foreach link $MV(server_links) {
			if {$x >= $MV(server_links.$link.x1) &&
				$x <= $MV(server_links.$link.x2) &&
				$y >= $MV(server_links.$link.y1) &&
				$y <= $MV(server_links.$link.y2)} {
				DebugIt "Openening URL $MV(server_links.$link.url)" other
				set MV(url.-1.-99.what) $MV(server_links.$link.url)
				URL_OpenWeb -1 -99
				set ret 1
			}
		}
		if !$ret {
			foreach tell $MV(server_tells) {
				if {$x >= $MV(server_tells.$tell.x1) &&
					$x <= $MV(server_tells.$tell.x2) &&
					$y >= $MV(server_tells.$tell.y1) &&
					$y <= $MV(server_tells.$tell.y2)} {
					DebugIt "Telling Server $tell" other
					SendToServer "TELL $tell"
					set ret 1
				}
			}
		}
		if !$ret {
			foreach link $MV(server_exits) {
				if {$x >= $MV(server_exits.$link.x1) &&
					$x <= $MV(server_exits.$link.x2) &&
					$y >= $MV(server_exits.$link.y1) &&
					$y <= $MV(server_exits.$link.y2)} {
					DebugIt "Clicked Exit $MV(server_exits.$link.host)" other
					ConnectToRoom $MV(server_exits.$link.host) $MV(server_exits.$link.port)
					set ret 1
					break
				}
			}
		}
	}

	if !$ret {
		SendToServer "MOVE $MV(nick) $x $y $MV(movespeed)"
		set string "$x $y $MV(x) $MV(y) $MV(movespeed) 1 0"
		set MV(x) $x
		set MV(y) $y
		update idletasks
		lappend MV($MV(nick).moves) $string
		MoveAvatars 0
	}
}

proc DoTheMove {person} {
	global MV

	set length [llength $MV($person.moves)]
	if !$length {return}
	set info [split [lindex $MV($person.moves) 0] " "]
	set delete_flag [lindex $info 6]
	if $delete_flag {return}
	set x [lindex $info 0]
	set y [lindex $info 1]
	set xx [lindex $info 2]
	set yy [lindex $info 3]
	set speed [lindex $info 4]
	set nametag [lindex $info 5]
	if {![string compare $person $MV(nick)]} {
		set image $MV(img)
	} else {
		set image $MV($person.icon)
	}
	set MV($person.moving) 1
	set sspeed [expr $speed * 3]

	#puts "DoTheMove $x $y $xx $yy $speed $nametag $delete_flag"

	if {[expr $x - $xx] > 0} {set xdir 1} else {set xdir -1}
	if {[expr $y - $yy] > 0} {set ydir 1} else {set ydir -1}
	if {[expr $y - $yy] == 0} {set ydir 0}
	if {[expr $x - $xx] == 0} {set xdir 0}

	set xinc $xdir
	set yinc $ydir
	
	if {$xdir && $sspeed != 2} {
		set xinc [expr $xdir * 1]
		set xxx $xx
		while { $xxx != $x && $xinc != [expr $sspeed * $xdir]} {
			set xxx $xx
			incr xinc [expr $xdir * 1]
			incr xxx $xinc
		}
	}
	if {$ydir && $sspeed != 2} {
		set yinc [expr $ydir * 1]
		set yyy $yy
		while { $yyy != $y && $yinc != [expr $sspeed * $ydir]} {
			set yyy $yy
			incr yinc [expr $ydir * 1]
			incr yyy $yinc
		}
	}
	.top.c move $image $xinc $yinc
	set xx [expr $xx + $xinc]
	set yy [expr $yy + $yinc]

	set MV($person.current_x) $xx
	set MV($person.current_y) $yy

	if {$nametag && $MV(names)} {
		KillName $person
		ShowName $person
	}

	if {$x != $xx || $y != $yy} {
		set newlist "$x $y $xx $yy $speed $nametag 0"
	} else {
		set newlist "$x $y $xx $yy $speed $nametag 1"
	}
	set MV($person.moves) [lreplace $MV($person.moves) 0 0 $newlist]
	# Process Plugins!
	#
	foreach plugin $MV(plugin.traps.MoveTo.Post) {
		$MV(plugin.traps.MoveTo.Post.$plugin) $x $y
	}
}

proc MoveAvatars {loop_var} {
	global MV

	if {$MV(moving) && !$loop_var} {return}
	set MV(moving) 1

	# Info needed for movement.
	#
	# X position to move to.
	# Y position to move to.
	# XX Original X position
	# YY Original Y position
	# Speed at which we move.
	# Nametag replacement (should we move the nametag)
	# The image name to move.

	set anymove 0
	set moveusers {}
	if {[llength $MV($MV(nick).moves)]} {
		lappend moveusers $MV(nick)
	} else {
		set MV($MV(nick).moving) 0
	}
	foreach person $MV(people) {
		if {[llength $MV($person.moves)]} {
			lappend moveusers $person
		} else {
			set MV($person.moving) 0
		}
	}
	foreach user $moveusers {
		DoTheMove $user
		set info [split [lindex $MV($user.moves) 0] " "]
		set delete_flag [lindex $info 6]
		if $delete_flag {
			set MV($user.moves) [lreplace $MV($user.moves) 0 0]
		}
		if {[llength $MV($user.moves)]} {
			set anymove 1
		} else {
			set MV($user.moving) 0
		}
	}
	update idletasks
	if $anymove {
		#
		# NOTE: PERFORMANCE!
		#
		# What you see below is a great performance enhancer. It
		# causes this function to return now instead of later and
		# prevents unnescary nesting.
		#
		after 1 "MoveAvatars 1"
	} else {
		set MV(moving) 0
	}
	return
}

proc ChangeUserAvatar {who what x y size bx by} {
	global MV

	if {[lsearch -exact $MV(people) $who] == -1} {return}
	if {[TestNum $x] || [TestNum $y] || [TestPosNum $size] || \
	[TestNum $bx] || [TestNum $by]} {
		DebugIt "ChangeUserAvatar: Invalid Parameters" other
		return
	}

	set MV($who.avatar) $what
	set MV($who.name_x_offset) $x
	set MV($who.name_y_offset) $y
	set MV($who.baloon_x) $bx
	set MV($who.baloon_y) $by
	if $MV(names) {
		KillName $who
		ShowName $who
	}
	if {![string compare $what "default.gif"]} {
		image create photo OpenVerse_User_Image_$who -file "$MV(images)/default.gif"
		return
	}
	if {![file exists "$MV(rem_images)/$what"]} {
		if {[lsearch -exact $MV(downloads) $what] == -1} {
			DebugIt "I do not have $what ([file exists \"$MV(rem_images)/$what\"])" other
			if $MV(download_avatars) {
				SendToServer "DCCSENDAV $what"
				set MV($who.downloading) 1
			}
		}
	} else {
		if {[file size "$MV(rem_images)/$what"] != $size} {
			if {[lsearch -exact $MV(downloads) $what] == -1} {
				DebugIt "size mismatch for $what" other
				catch {file delete "$MV(rem_images)/$what"}
				if $MV(download_avatars) {
					SendToServer "DCCSENDAV $what"
					set MV($who.downloading) 1
				}
			}
		} else {
			catch {image create photo OpenVerse_User_Image_$who -file "$MV(rem_images)/$what"} err
			if {[string compare $err OpenVerse_User_Image_$who]} {
				image create photo OpenVerse_User_Image_$who -file "$MV(images)/default.gif"
			}
		}
	}

	#
	# Process Plugins!
	#
	foreach plugin $MV(plugin.traps.ChangeUserAvatar) {
		if {![$MV(plugin.traps.ChangeUserAvatar.$plugin) $who $what $x $y $size $bx $by]} {return}
	}	
}

proc MoveUser {who x y speed} {
	global MV
	
	if {![string compare $who $MV(nick)]} {return}
	if {[lsearch -exact $MV(people) $who] == -1} {return}
	if $MV($who.nomoremove) {return}
	if {[TestNum $x] || [TestNum $y] || [TestNum $speed]} {
		DebugIt "MoveUser: Invalid Parameters" other
		return
	}
	#
	# Process Plugins!
	#
	foreach plugin $MV(plugin.traps.MoveUser.Pre) {
		if {![$MV(plugin.traps.MoveUser.Pre.$plugin) $who $x $y $speed]} {return}
	}	
	if {![string compare $speed ""]} {set speed 1}

	set string "$x $y $MV($who.x) $MV($who.y) $speed 1 0"
	set MV($who.x) $x
	set MV($who.y) $y
	update idletasks
	lappend MV($who.moves) $string
	MoveAvatars 0

	#
	# Process Plugins!
	#
	foreach plugin $MV(plugin.traps.MoveUser.Post) {
		if {![$MV(plugin.traps.MoveUser.Post.$plugin) $who $x $y $speed]} {return}
	}	
}

proc AvatarEffect {who what} {
	global MV

	if {![string compare $who $MV(nick)]} {
		set image $MV(img)
	} else {
		set image $MV($who.icon)
	}
	set x $MV($who.x)
	set y $MV($who.y)
	DebugIt "(Effect) - $who $what $x $y" other
	switch -- [string tolower $what] {
		"shiver" {
			set dir 0
			for {set c 0} {$c < 50} {incr c} {
				if $dir {
					set dir 0
					set xx [expr $x - 15]
				} else {
					set dir 1
					set xx [expr $x + 15]
				}
				lappend MV($who.moves) "$xx $y $x $y 12 0 0"
				lappend MV($who.moves) "$x $y $xx $y 12 0 0"
			}
			MoveAvatars 0
		}
		"jump" {
			set dir 0
			for {set c 0} {$c < 50} {incr c} {
				switch -- $dir {
					"0" {
						set dir 1
						set yy [expr $y - 15]
					}
					"1" {
						set dir 2
						set yy [expr $y - 30]
					}
					"2" {
						set dir 3
						set yy [expr $y - 45]
					}
					"3" {
						set dir 4
						set yy [expr $y - 30]
					}
					"4" {
						set dir 5
						set yy [expr $y - 15]
					}
					"5" {
						set dir 0
						set yy $y
					}
				}
				lappend MV($who.moves) "$x $yy $x $y 3 0 0"
				lappend MV($who.moves) "$x $y $x $yy 3 0 0"
			}
			MoveAvatars 0
		}
	}
}
