Back to index

gcompris  8.2.2
searace.py
Go to the documentation of this file.
00001 #  gcompris - searace
00002 #
00003 # Time-stamp: <2001/08/20 00:54:45 bruno>
00004 #
00005 # Copyright (C) 2004 Bruno Coudoin
00006 #
00007 #   This program is free software; you can redistribute it and/or modify
00008 #   it under the terms of the GNU General Public License as published by
00009 #   the Free Software Foundation; either version 2 of the License, or
00010 #   (at your option) any later version.
00011 #
00012 #   This program is distributed in the hope that it will be useful,
00013 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 #   GNU General Public License for more details.
00016 #
00017 #   You should have received a copy of the GNU General Public License
00018 #   along with this program; if not, write to the Free Software
00019 #   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00020 #
00021 import gnomecanvas
00022 import gcompris
00023 import gcompris.utils
00024 import gcompris.skin
00025 import gtk
00026 import gtk.gdk
00027 import random
00028 import math
00029 import time
00030 from gettext import gettext as _
00031 
00032 class Boat:
00033   """The Boat Class"""
00034 
00035   # The Text View and Scrolled windows
00036   tb          = None
00037   tv          = None
00038   sw          = None
00039   x           = 0.0
00040   y           = 0.0
00041   angle       = 0
00042   arrived     = False
00043   won         = False
00044   finish_time = 0
00045 
00046   # To move the ship
00047   dx          = 0.0
00048   dy          = 0.0
00049 
00050   # The user commands parsing
00051   line        = 0
00052   # The boat item
00053   item        = []
00054   player      = 0
00055 
00056   # Store a timer object
00057   timer       = 0
00058 
00059   # Weather condition cache to avoid to find it each time
00060   condition   = []
00061 
00062   # Display the speed here
00063   speeditem   = []
00064 
00065 class Gcompris_searace:
00066   """The Boat Racing activity"""
00067 
00068 
00069   def __init__(self, gcomprisBoard):
00070     self.gcomprisBoard = gcomprisBoard
00071 
00072     self.board_paused = False
00073 
00074     # Some constants
00075     self.border_x  = 30
00076     self.sea_area = (self.border_x , 30, gcompris.BOARD_WIDTH-self.border_x , 350)
00077     self.weather   = []
00078 
00079     self.left_boat  = Boat()
00080     self.left_boat.player = 0
00081 
00082     self.right_boat = Boat()
00083     self.left_boat.player = 1
00084 
00085     self.left_initial_boat_y  = 150
00086     self.right_initial_boat_y = 150 + 28
00087 
00088     # The basic tick for object moves
00089     self.timerinc = 40
00090     self.timer_turn = 15
00091     self.timer1 = 0
00092     self.timer2 = 0
00093 
00094     # How to transform user visible sea size to pixels (calculated later)
00095     self.sea_ratio = 1
00096 
00097     # We display what's going on here
00098     self.statusitem = []
00099 
00100     #print("Gcompris_searace __init__.")
00101 
00102 
00103   def start(self):
00104     self.gcomprisBoard.level=1
00105     self.gcomprisBoard.maxlevel=4
00106     self.gcomprisBoard.sublevel=1
00107     self.gcomprisBoard.number_of_sublevel=1
00108 
00109     self.board_paused = False
00110 
00111     pixmap = gcompris.utils.load_pixmap(gcompris.skin.image_to_skin("button_reload.png"))
00112     if(pixmap):
00113       gcompris.bar_set_repeat_icon(pixmap)
00114       gcompris.bar_set(gcompris.BAR_OK|gcompris.BAR_LEVEL|gcompris.BAR_REPEAT_ICON)
00115     else:
00116       gcompris.bar_set(gcompris.BAR_OK|gcompris.BAR_LEVEL|gcompris.BAR_REPEAT);
00117 
00118 
00119     gcompris.bar_set_level(self.gcomprisBoard)
00120 
00121     # Create our rootitem. We put each canvas item in it so at the end we
00122     # only have to kill it. The canvas deletes all the items it contains automaticaly.
00123     self.rootitem = self.gcomprisBoard.canvas.root().add(
00124       gnomecanvas.CanvasGroup,
00125       x=0.0,
00126       y=0.0
00127       )
00128 
00129     pixmap = gcompris.utils.load_pixmap(gcompris.skin.image_to_skin("gcompris-bg.jpg"))
00130     item = self.rootitem.add(
00131       gnomecanvas.CanvasPixbuf,
00132       pixbuf = pixmap,
00133       x=0,
00134       y=0,
00135       )
00136     item.connect("event", self.ruler_item_event)
00137 
00138     self.display_sea_area()
00139 
00140     self.root_weather_item = self.rootitem.add(
00141       gnomecanvas.CanvasGroup,
00142       x=0.0,
00143       y=0.0
00144       )
00145 
00146     # Display the weather now
00147     self.display_weather()
00148 
00149     # And finaly the players boats
00150     self.init_boats()
00151 
00152     #print("Gcompris_searace start.")
00153 
00154 
00155   def end(self):
00156     # Remove all the timer first
00157     if self.timer1 :
00158       gtk.timeout_remove(self.timer1)
00159 
00160     if self.timer2 :
00161       gtk.timeout_remove(self.timer2)
00162 
00163     if self.left_boat.timer :
00164       gtk.timeout_remove(self.left_boat.timer)
00165 
00166     if self.right_boat.timer :
00167       gtk.timeout_remove(self.right_boat.timer)
00168 
00169     # Remove the root item removes all the others inside it
00170     self.rootitem.destroy()
00171 
00172     #print("Gcompris_searace end.")
00173 
00174 
00175   def pause(self, pause):
00176 
00177     self.board_paused = pause
00178 
00179     # There is a problem with GTK widgets, they are not covered by the help
00180     # We hide/show them here
00181     if(pause):
00182       self.left_boat.sw.hide()
00183       self.right_boat.sw.hide()
00184     else:
00185       self.left_boat.sw.show()
00186       self.right_boat.sw.show()
00187       self.repeat()
00188 
00189     return
00190 
00191   def ok(self):
00192     # This is a real go
00193     # We set a timer. At each tick an entry in each user box is read analysed and run
00194     if(not self.left_boat.timer and not self.right_boat.timer):
00195       self.left_boat.tv.set_editable(False)
00196       self.right_boat.tv.set_editable(False)
00197       self.race_one_command(self.left_boat)
00198       self.race_one_command(self.right_boat)
00199     else:
00200       self.statusitem.set(text=_("The race is already being run"))
00201 
00202   # Called by gcompris when the user click on the level icon
00203   def set_level(self, level):
00204     if(self.left_boat.timer or self.right_boat.timer):
00205       self.statusitem.set(text=_("The race is already being run"))
00206     else:
00207       self.gcomprisBoard.level=level;
00208       self.gcomprisBoard.sublevel=1;
00209 
00210       # Set the level in the control bar
00211       gcompris.bar_set_level(self.gcomprisBoard);
00212 
00213       # Remove the root item removes all the others inside it
00214       self.root_weather_item.destroy()
00215 
00216       self.root_weather_item = self.rootitem.add(
00217         gnomecanvas.CanvasGroup,
00218         x=0.0,
00219         y=0.0
00220         )
00221 
00222       # Display the weather now
00223       self.display_weather()
00224 
00225       self.init_boats()
00226 
00227 
00228   def repeat(self):
00229     # Want to rerun it
00230     if(self.left_boat.timer or self.right_boat.timer):
00231       self.statusitem.set(text=_("The race is already being run"))
00232     else:
00233       self.init_boats()
00234 
00235 
00236   def config(self):
00237     #print("Gcompris_searace config.")
00238     return
00239 
00240   def key_press(self, keyval, commit_str, preedit_str):
00241     #print("got key %i" % keyval)
00242     return False
00243 
00244   # ----------------------------------------------------------------------
00245   # ----------------------------------------------------------------------
00246   # ----------------------------------------------------------------------
00247 
00248   # Set the initial coordinates of the boats and display them
00249   def init_boats(self):
00250 
00251     self.left_boat.x      = self.border_x
00252     self.left_boat.y      = self.left_initial_boat_y
00253     self.right_boat.x     = self.left_boat.x
00254     self.right_boat.y     = self.right_initial_boat_y
00255     self.left_boat.angle  = 0
00256     self.right_boat.angle = 0
00257 
00258     # Display the player boats
00259     if(self.left_boat.item):
00260       self.left_boat.item.destroy()
00261 
00262     pixmap = gcompris.utils.load_pixmap("images/top_boat_red.png")
00263     self.left_boat.item = self.rootitem.add(
00264       gnomecanvas.CanvasPixbuf,
00265       pixbuf = pixmap,
00266       x=self.left_boat.x,
00267       y=self.left_boat.y,
00268       anchor=gtk.ANCHOR_CENTER,
00269       )
00270     self.left_boat.item.raise_to_top()
00271     self.left_boat.item.connect("event", self.ruler_item_event)
00272 
00273     if(self.right_boat.item):
00274       self.right_boat.item.destroy()
00275 
00276     pixmap = gcompris.utils.load_pixmap("images/top_boat_green.png")
00277     self.right_boat.item = self.rootitem.add(
00278       gnomecanvas.CanvasPixbuf,
00279       pixbuf = pixmap,
00280       x=self.right_boat.x,
00281       y=self.right_boat.y,
00282       anchor=gtk.ANCHOR_CENTER,
00283       )
00284     self.right_boat.item.raise_to_top()
00285     self.right_boat.item.connect("event", self.ruler_item_event)
00286 
00287 
00288     # Reset command line processing as well.
00289     self.left_boat.line     = 0
00290     self.right_boat.line    = 0
00291     self.left_boat.arrived  = False
00292     self.right_boat.arrived = False
00293     self.left_boat.won      = False
00294     self.right_boat.won     = False
00295     self.statusitem.set(text="")
00296 
00297     # Let the user enter comands
00298     self.left_boat.tv.set_editable(True)
00299 
00300     if self.gcomprisBoard.mode == '1player':
00301       self.tux_move()
00302     else:
00303       self.right_boat.tv.set_editable(True)
00304 
00305 
00306   #----------------------------------------
00307   # Display the whole playing field
00308   # This is called once only
00309   def display_sea_area(self):
00310     # Some constant to define the sea area
00311     # The sea area is defined in the global self.sea_area
00312     step_x    = (self.sea_area[2]-self.sea_area[0])/20
00313     step_y    = (self.sea_area[3]-self.sea_area[1])/10
00314     self.sea_ratio = step_x
00315 
00316     text_x    = self.sea_area[0] - 15
00317     text_y    = self.sea_area[1] - 15
00318 
00319     # We manage a 2 colors grid
00320     ci = 0
00321     ca = 0xAACCFFFFL
00322     cb = 0x1D0DFFFFL
00323 
00324     for y in range (self.sea_area[1], self.sea_area[3]+1, int(step_y)):
00325       if(ci%2):
00326         color = ca
00327       else:
00328         color = cb
00329       ci += 1
00330 
00331       # Shadow for text number
00332       item = self.rootitem.add (
00333         gnomecanvas.CanvasText,
00334         text=int(ci),
00335         font=gcompris.skin.get_font("gcompris/content"),
00336         x=text_x+1,
00337         y=y+1,
00338         fill_color_rgba=0x000000FFL
00339         )
00340       item.connect("event", self.ruler_item_event)
00341 
00342       # Text number
00343       item = self.rootitem.add (
00344         gnomecanvas.CanvasText,
00345         text=int(ci),
00346         font=gcompris.skin.get_font("gcompris/content"),
00347         x=text_x,
00348         y=y,
00349         fill_color_rgba=cb
00350         )
00351       item.connect("event", self.ruler_item_event)
00352 
00353       item = self.rootitem.add(
00354         gnomecanvas.CanvasLine,
00355         points=(self.sea_area[0], y, self.sea_area[2], y),
00356         fill_color_rgba = color,
00357          width_units=1.0
00358         )
00359       item.connect("event", self.ruler_item_event)
00360 
00361 
00362     ci = 0
00363     for x in range (self.sea_area[0], self.sea_area[2]+1, int(step_x)):
00364       if(ci%2):
00365         color = ca
00366       else:
00367         color = cb
00368       ci += 1
00369 
00370       # Shadow for text number
00371       item = self.rootitem.add (
00372         gnomecanvas.CanvasText,
00373         text=int(ci),
00374         font=gcompris.skin.get_font("gcompris/content"),
00375         x=x+1,
00376         y=text_y+1,
00377         fill_color_rgba=0x000000FFL
00378         )
00379       item.connect("event", self.ruler_item_event)
00380 
00381       # Text number
00382       item = self.rootitem.add (
00383         gnomecanvas.CanvasText,
00384         text=int(ci),
00385         font=gcompris.skin.get_font("gcompris/content"),
00386         x=x,
00387         y=text_y,
00388         fill_color_rgba=cb
00389         )
00390       item.connect("event", self.ruler_item_event)
00391 
00392       item = self.rootitem.add(
00393         gnomecanvas.CanvasLine,
00394         points=(x, self.sea_area[1], x, self.sea_area[3]),
00395         fill_color_rgba = color,
00396          width_units=1.0
00397         )
00398       item.connect("event", self.ruler_item_event)
00399 
00400 
00401     # The ARRIVAL LINE
00402     item = self.rootitem.add(
00403       gnomecanvas.CanvasLine,
00404       points=(self.sea_area[2], self.sea_area[1]-5, self.sea_area[2], self.sea_area[3]+5),
00405       fill_color_rgba = 0xFF0000FFL,
00406       width_units=5.0
00407       )
00408     item.connect("event", self.ruler_item_event)
00409 
00410     # The grid is done
00411     # ----------------
00412 
00413     # The Programming input area LEFT
00414     self.left_boat.sw = gtk.ScrolledWindow()
00415     self.left_boat.sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)
00416     self.left_boat.sw.set_shadow_type(gtk.SHADOW_ETCHED_OUT)
00417 
00418     w = 250.0
00419     h = 100.0
00420     y = 400.0 # The upper limit of the text boxes
00421     x_left  = gcompris.BOARD_WIDTH/4 - 30
00422     x_right = (gcompris.BOARD_WIDTH/4)*3 + 30
00423 
00424     self.left_boat.tb = gtk.TextBuffer()
00425     self.left_boat.tv = gtk.TextView(self.left_boat.tb)
00426     self.left_boat.sw.add(self.left_boat.tv)
00427 
00428     command_example = _("right") + " 45\n" + _("forward") + " 5\n" + _("left") + " 45"
00429     self.left_boat.tb.set_text(command_example)
00430 
00431     self.left_boat.tv.set_wrap_mode(gtk.WRAP_CHAR)
00432     self.rootitem.add(
00433       gnomecanvas.CanvasWidget,
00434       widget=self.left_boat.sw,
00435       x=x_left,
00436       y=y,
00437       width=w,
00438       height= h,
00439       anchor=gtk.ANCHOR_N,
00440       size_pixels=False)
00441     self.left_boat.tv.show()
00442     self.left_boat.sw.show()
00443 
00444     # The Programming input area RIGHT
00445     self.right_boat.sw = gtk.ScrolledWindow()
00446     self.right_boat.sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)
00447     self.right_boat.sw.set_shadow_type(gtk.SHADOW_ETCHED_OUT)
00448 
00449     self.right_boat.tb = gtk.TextBuffer()
00450     self.right_boat.tv = gtk.TextView(self.right_boat.tb)
00451     self.right_boat.sw.add(self.right_boat.tv)
00452 
00453     command_example = _("left") + " 45\n" + _("forward") + " 5\n" + _("right") + " 45"
00454     self.right_boat.tb.set_text(command_example)
00455 
00456     self.right_boat.tv.set_wrap_mode(gtk.WRAP_CHAR)
00457     self.rootitem.add(
00458       gnomecanvas.CanvasWidget,
00459       widget=self.right_boat.sw,
00460       x=x_right,
00461       y=y,
00462       width=w,
00463       height= h,
00464       anchor=gtk.ANCHOR_N,
00465       size_pixels=False)
00466     self.right_boat.tv.show()
00467     self.right_boat.sw.show()
00468 
00469     # Text Labels
00470     self.left_boat.speeditem = self.rootitem.add (
00471       gnomecanvas.CanvasText,
00472       text="",
00473       font=gcompris.skin.get_font("gcompris/content"),
00474       x=x_left,
00475       y=y-20,
00476       fill_color_rgba=0xFF0000FFL
00477       )
00478 
00479     self.right_boat.speeditem = self.rootitem.add (
00480       gnomecanvas.CanvasText,
00481       text="",
00482       font=gcompris.skin.get_font("gcompris/content"),
00483       x=x_right,
00484       y=y-20,
00485       fill_color_rgba=0X027308FFL
00486       )
00487 
00488     # The status area
00489     self.statusitem = self.rootitem.add (
00490       gnomecanvas.CanvasText,
00491       text="",
00492       font=gcompris.skin.get_font("gcompris/content"),
00493       x=gcompris.BOARD_WIDTH/2,
00494       y=y-40,
00495       fill_color_rgba=0X000a89FFL
00496       )
00497 
00498     # The decoration boats
00499     pixmap = gcompris.utils.load_pixmap("images/top_boat_red.png")
00500     item = self.rootitem.add(
00501       gnomecanvas.CanvasPixbuf,
00502       pixbuf = pixmap,
00503       x=25,
00504       y=y+40,
00505       anchor=gtk.ANCHOR_CENTER,
00506       )
00507     gcompris.utils.item_rotate_relative(item, -90);
00508 
00509     pixmap = gcompris.utils.load_pixmap("images/top_boat_green.png")
00510     item = self.rootitem.add(
00511       gnomecanvas.CanvasPixbuf,
00512       pixbuf = pixmap,
00513       x=gcompris.BOARD_WIDTH-25,
00514       y=y+40,
00515       anchor=gtk.ANCHOR_CENTER,
00516       )
00517     gcompris.utils.item_rotate_relative(item, -90);
00518 
00519     # The commands
00520     hl = 18
00521     y += 7
00522     text_color = 0x0000FFFFL
00523     self.rootitem.add (
00524       gnomecanvas.CanvasText,
00525       text=_("COMMANDS ARE"),
00526       font=gcompris.skin.get_font("gcompris/content"),
00527       x=gcompris.BOARD_WIDTH/2,
00528       y=y,
00529       fill_color_rgba=text_color
00530       )
00531 
00532     self.rootitem.add (
00533       gnomecanvas.CanvasText,
00534       text=_("forward"),
00535       font=gcompris.skin.get_font("gcompris/content"),
00536       x=gcompris.BOARD_WIDTH/2,
00537       y=y+hl,
00538       fill_color_rgba=text_color
00539       )
00540 
00541     self.rootitem.add (
00542       gnomecanvas.CanvasText,
00543       text=_("left"),
00544       font=gcompris.skin.get_font("gcompris/content"),
00545       x=gcompris.BOARD_WIDTH/2,
00546       y=y+hl*2,
00547       fill_color_rgba=text_color
00548       )
00549 
00550     self.rootitem.add (
00551       gnomecanvas.CanvasText,
00552       text=_("right"),
00553       font=gcompris.skin.get_font("gcompris/content"),
00554       x=gcompris.BOARD_WIDTH/2,
00555       y=y+hl*3,
00556       fill_color_rgba=text_color
00557       )
00558 
00559 
00560   # Weather condition is a 2 value pair (angle wind_speed)
00561   # Weather is a list of the form:
00562   # (rectangle coordinate) (weather)
00563   def display_weather(self):
00564 
00565     # Reset the weather list
00566     self.weather   = []
00567 
00568     # Some constant to define the sea area
00569     # The sea area is defined in the global self.sea_area
00570     slice_x = 5 + self.gcomprisBoard.level
00571     slice_y = 3 + self.gcomprisBoard.level
00572 
00573     step_x  = (self.sea_area[2]-self.sea_area[0])/slice_x
00574     step_y  = (self.sea_area[3]-self.sea_area[1])/slice_y
00575 
00576     stop_x  = self.sea_area[0]+step_x*slice_x
00577     stop_y  = self.sea_area[1]+step_y*slice_y
00578 
00579     for x in range (self.sea_area[0], stop_x, int(step_x)):
00580       for y in range (self.sea_area[1], stop_y, int(step_y)):
00581         #print x, step_x, self.sea_area[2]
00582         angle = 0
00583         if(self.left_initial_boat_y>y and self.left_initial_boat_y<y+step_y):
00584           # Bad weather condition on the straigh line
00585           direction = random.randint(5,7)
00586         elif(self.right_initial_boat_y>y and self.right_initial_boat_y<y+step_y):
00587           # Bad weather condition on the straigh line
00588           direction = random.randint(5,7)
00589         else:
00590           direction = random.randint(0,7)
00591 
00592         if(direction < 4):
00593           # There is more chance to go forward than others
00594           angle = random.randint(-45,45)
00595         elif(direction < 5):
00596           angle = random.randint(135,225)
00597         elif(direction == 6):
00598           angle = random.randint(80, 110)
00599         elif(direction == 7):
00600           angle = random.randint(260, 280)
00601 
00602         speed = random.randint(1,10)
00603         condition = [ (x, y, x+step_x, y+step_y), (angle, speed) ]
00604         self.display_condition(condition)
00605         self.weather.append(condition)
00606 
00607 
00608     return
00609 
00610   # Display the given weather condition
00611   def display_condition(self, condition):
00612 
00613     #print condition
00614     # Calc the center
00615     cx = condition[0][0]+(condition[0][2]-condition[0][0])/2
00616     cy = condition[0][1]+(condition[0][3]-condition[0][1])/2
00617 
00618     pixmap = gcompris.utils.load_pixmap("images/arrow.png")
00619     item = self.root_weather_item.add(
00620       gnomecanvas.CanvasPixbuf,
00621       pixbuf = pixmap,
00622       x=cx,
00623       y=cy,
00624       anchor=gtk.ANCHOR_CENTER
00625       )
00626     gcompris.utils.item_rotate_relative(item, condition[1][0]);
00627     item.connect("event", self.ruler_item_event)
00628 
00629     # Text number Shadow
00630     item = self.root_weather_item.add (
00631       gnomecanvas.CanvasText,
00632       text=condition[1][1],
00633       font=gcompris.skin.get_font("gcompris/content"),
00634       x=cx+1+pixmap.get_width()/2,
00635       y=cy+1+pixmap.get_height()/2,
00636       fill_color_rgba=0x000000FFL
00637       )
00638     item.connect("event", self.ruler_item_event)
00639 
00640     # Text number
00641     item = self.root_weather_item.add (
00642       gnomecanvas.CanvasText,
00643       text=condition[1][1],
00644       font=gcompris.skin.get_font("gcompris/content"),
00645       x=cx+pixmap.get_width()/2,
00646       y=cy+pixmap.get_height()/2,
00647       fill_color_rgba=0xFFFFFFFFL
00648       )
00649     item.connect("event", self.ruler_item_event)
00650 
00651     return
00652 
00653   # Given a boat item, return it's weather condition
00654   def get_weather_condition(self, boat):
00655     (x, y)= boat.item.i2w( boat.x, boat.y)
00656 
00657     # Look in the cache to speed the process
00658     if(boat.condition):
00659       coord = boat.condition[0]
00660       condition = boat.condition[1]
00661       if(boat.x >= coord[0] and boat.x <= coord[2] and boat.y >= coord[1] and boat.y <= coord[3]):
00662         return(condition)
00663 
00664     for conditions in self.weather:
00665       coord = conditions[0]
00666       condition = conditions[1]
00667       #print "Testing coord="+str(coord)+" Boat coord x="+str(x)+" y="+str(y)
00668       if(x >= coord[0] and x <= coord[2] and y >= coord[1] and y <= coord[3]):
00669         boat.condition = conditions
00670         #print "Found: x="+str(x)+" y="+str(y)+" coord="+str(coord)
00671         #print "  angle=" + str(int(condition[0])) + "  speed=" + str(condition[1])
00672         return(condition)
00673     # Should not happen, return a normal condition anyway
00674     return(0,1)
00675 
00676   # Given a x y coord, return it's weather condition (no caching)
00677   def get_absolute_weather_condition(self, x, y):
00678     for conditions in self.weather:
00679       coord = conditions[0]
00680       condition = conditions[1]
00681       #print "Testing coord="+str(coord)+" Boat coord x="+str(x)+" y="+str(y)
00682       if(x >= coord[0] and x <= coord[2] and y >= coord[1] and y <= coord[3]):
00683         #print "Found: x="+str(x)+" y="+str(y)+" coord="+str(coord)
00684         #print "  angle=" + str(int(condition[0])) + "  speed=" + str(condition[1])
00685         return(condition)
00686     # Should not happen, return a normal condition anyway
00687     return(0,1)
00688 
00689   # Return a wind score depending on an angle
00690   def get_wind_score(self, boat_angle, condition):
00691     # Calculate the timer inc depending on the wind angle + speed
00692     wind_angle = condition[0] - boat_angle
00693     if(wind_angle>360):
00694       wind_angle-=360
00695     elif(wind_angle<-360):
00696       wind_angle+=360
00697 
00698     if(abs(wind_angle)>180):
00699       wind_angle=180-(abs(wind_angle)-180)
00700 
00701     # Increase the timer depending on wind force and direction
00702     angle_pi = wind_angle*math.pi/180
00703 
00704     cx = math.cos(angle_pi)
00705     penalty=3
00706     if(cx<0):
00707       penalty*=2
00708 
00709     return(cx*condition[1]*-1*penalty)
00710 
00711   #
00712   # Boat moving
00713   # -----------
00714   def cmd_forward(self, boat, value):
00715     #    print "Player " + str(boat.player) + " cmd_forward " + str(value) + " dx=" + str(boat.dx) + " dy=" + str(boat.dy)
00716 
00717     if(self.board_paused):
00718       boat.timer = 0
00719       return
00720 
00721     value -= 1
00722     if value <= 0:
00723       # Process next command
00724       self.race_one_command(boat)
00725       boat.timer = 0
00726       return
00727 
00728     # Move it
00729     boat.x += 1
00730     boat.y += 0
00731 
00732     # We need to convert the coord to the rootitem coordinate to check limits
00733     (x, y)= boat.item.i2w( boat.x, boat.y)
00734 
00735     # Manage the wrapping
00736     if(y<self.sea_area[1]):
00737       y = self.sea_area[3]
00738       (boat.x, boat.y)= boat.item.w2i( x, y)
00739     elif(y>self.sea_area[3]):
00740       y = self.sea_area[1]
00741       (boat.x, boat.y)= boat.item.w2i( x, y)
00742     elif(x>self.sea_area[2]):
00743       boat.arrived     = True
00744       boat.finish_time = time.time()
00745       print "self.left_boat.finish_time" + str(self.left_boat.finish_time)
00746       print "self.right_boat.finish_time" + str(self.right_boat.finish_time)
00747       if(not self.left_boat.won and not self.right_boat.won):
00748         boat.won = True
00749       elif(abs(self.left_boat.finish_time - self.right_boat.finish_time) < 1):
00750         # The two boat arrived in a close time frame (1s), it's a draw
00751         self.statusitem.set(text=_("This is a draw"))
00752         self.left_boat.won  = False
00753         self.right_boat.won = False
00754         boat.speeditem.set(text="")
00755         boat.speeditem.set(text="")
00756 
00757       if(self.left_boat.won):
00758         self.statusitem.set(text=_("The Red boat has won"))
00759         boat.speeditem.set(text="")
00760       elif(self.right_boat.won):
00761         self.statusitem.set(text=_("The Green boat has won"))
00762         boat.speeditem.set(text="")
00763 
00764       boat.timer = 0
00765       return
00766 
00767     condition = self.get_weather_condition(boat)
00768 
00769     boat.item.set(x = boat.x,
00770                   y = boat.y)
00771 
00772 
00773     wind = self.get_wind_score(boat.angle, condition)
00774 
00775     #print "Player " + str(boat.player) + "  wind_angle=" + str(abs(wind_angle)) + " condition=" + str(condition[1]) + " cx=" + str(cx) + "     wind=" + str(wind)
00776     angle = condition[0]
00777     if(angle>180):
00778       angle = abs(angle-360)
00779     boat.speeditem.set(text = _("Angle:") + str(angle) + " " + _("Wind:") + str(int(wind)*-1))
00780     boat.timer = gtk.timeout_add(int(self.timerinc+wind), self.cmd_forward, boat, value)
00781 
00782 
00783 
00784 
00785   # Counter Clock wise rotation (use negative param to turn clock wise)
00786   def cmd_turn_left(self, boat, value):
00787     #    print "Player " + str(boat.player) + " turn left " + str(value)
00788 
00789     if(self.board_paused):
00790       boat.timer = 0
00791       return
00792 
00793     if value == 0:
00794       # Process next command
00795       self.race_one_command(boat)
00796       boat.timer = 0
00797       return
00798 
00799     turn = 1
00800     if value > 0:
00801       turn = -1
00802 
00803     boat.angle += turn
00804     if(boat.angle>360):
00805       boat.angle-=360
00806     elif(boat.angle<360):
00807       boat.angle+=360
00808 
00809     value    += turn
00810 
00811     gcompris.utils.item_rotate_relative(boat.item, turn);
00812     boat.timer = gtk.timeout_add(self.timer_turn, self.cmd_turn_left, boat, value)
00813 
00814   # Run the race
00815   def race_one_command(self, boat):
00816 
00817     if(self.board_paused):
00818       # Let the user enter commands
00819       boat.tv.set_editable(True)
00820 
00821       boat.line = 0
00822       boat.timer = 0
00823       return
00824 
00825     valid_cmd = False
00826     cmd = ""
00827     while(boat.line < boat.tb.get_line_count() and not valid_cmd):
00828       a = boat.tb.get_iter_at_line(boat.line)
00829       b = boat.tb.get_iter_at_line(boat.line)
00830       b.forward_to_line_end()
00831       cmd   = boat.tb.get_text(a, b, False)
00832       boat.line+=1
00833       if(cmd and cmd[0] == "\n"):
00834         boat.line+=1
00835       print "Processing cmd = " + cmd
00836       cmd   = cmd.strip("\n\t ")
00837       if(cmd != "" and cmd[0] != "#"):
00838         valid_cmd = True
00839 
00840     # No more commands to process for this player
00841     if (boat.line > boat.tb.get_line_count() or not valid_cmd):
00842       # Let the user enter commands
00843       boat.tv.set_editable(True)
00844       # Ready to Restart
00845       boat.line = 0
00846       boat.timer = 0
00847       return
00848 
00849     #print "boat.line=" + str(boat.line)
00850     #print "Parsing command = " + cmd + "<<"
00851     cmds  = cmd.split()
00852     # Manage default cases (no params given)
00853     if ( len(cmds) == 1 and cmd.startswith(_("forward")) ):
00854       cmd += " 1"
00855     elif ( len(cmds) == 1 and cmd.startswith(_("left")) ):
00856       cmd += " 45"
00857     elif ( len(cmds) == 1 and cmd.startswith(_("right")) ):
00858       cmd += " 45"
00859     elif ( len(cmds) > 2):
00860       boat.speeditem.set(text=_("Syntax error at line") + " " + str(boat.line) + "\n(" + cmd + ")")
00861 
00862       # Let the user enter commands
00863       boat.tv.set_editable(True)
00864 
00865       boat.line = 0
00866       boat.timer = 0
00867       return
00868 
00869     value = 0
00870     if(len(cmds) == 2):
00871       try:
00872         value = int(cmd.split()[1])
00873       except ValueError:
00874         # Let the user enter commands
00875         boat.tv.set_editable(True)
00876 
00877         boat.timer = 0
00878         boat.speeditem.set(text=_("The command") + " '" + cmd.split()[0] + "' " + "at line" + " " + str(boat.line) + "\n" + "requires a number parameter")
00879         boat.line = 0
00880         return
00881 
00882     if( cmd.startswith(_("forward"))):
00883       # Transform the value from user visible sea size to pixels
00884       value *= self.sea_ratio
00885 
00886       # Initialize the move
00887       boat.timer = gtk.timeout_add(self.timerinc, self.cmd_forward, boat, value)
00888     elif( cmd.startswith(_("left"))):
00889       boat.timer = gtk.timeout_add(self.timerinc, self.cmd_turn_left, boat, value)
00890     elif( cmd.startswith(_("right"))):
00891       boat.timer = gtk.timeout_add(self.timerinc, self.cmd_turn_left, boat, value*-1)
00892     else:
00893       # Let the user enter commands
00894       boat.tv.set_editable(True)
00895 
00896       boat.timer = 0
00897       boat.speeditem.set(text=_("Unknown command at line") + " " + str(boat.line) + "\n(" +  cmd.split()[0] + ")")
00898       boat.line = 0
00899 
00900   # Will return a text string: the tux move
00901   def tux_move(self):
00902 
00903     # The sea area is defined in the global self.sea_area
00904     step_x    = (self.sea_area[2]-self.sea_area[0])/20*2
00905     step_y    = (self.sea_area[3]-self.sea_area[1])/10
00906 
00907     # Original boat position
00908     bx     = self.right_boat.x
00909     by     = self.right_boat.y
00910     ba     = 0
00911     one_path = []
00912 
00913     # X loop
00914     while (bx <= self.sea_area[2] and bx >= self.sea_area[0]):
00915       score = 10000
00916       # By default, go straight
00917       coord = (bx, by, ba, int(step_x))
00918 
00919       # angle loop
00920       for boat_angle in [ -45, 0, 45 ]:
00921         a_pi = boat_angle * math.pi/180
00922         # Find the point from current boat position with angle a and distance step_x
00923         x = bx + math.cos(a_pi)*step_x
00924         y = by + math.sin(a_pi)*step_x
00925 
00926         # Manage the wrapping
00927         line_style = gtk.gdk.LINE_SOLID
00928         if(y<self.sea_area[1]):
00929           y = self.sea_area[3] - (self.sea_area[1]-y)
00930           line_style = gtk.gdk.LINE_DOUBLE_DASH
00931         elif(y>self.sea_area[3]):
00932           y = self.sea_area[1] + (y - self.sea_area[3])
00933           line_style = gtk.gdk.LINE_DOUBLE_DASH
00934 
00935         # Find shortest path to previous calculated point
00936         condition = self.get_absolute_weather_condition(x, y)
00937         wind = self.get_wind_score(boat_angle, condition)
00938         if(wind<score):
00939           score = wind
00940           coord = (x, y, boat_angle, step_x, line_style) # x y angle distance line_style
00941 
00942         self.root_weather_item.add(
00943           gnomecanvas.CanvasText, text=int(wind),
00944           y=y, x=x,
00945           fill_color_rgba=0xFF0000FFL,
00946           )
00947 
00948       # ----------
00949       self.root_weather_item.add(
00950         gnomecanvas.CanvasLine,
00951         points=(bx, by, coord[0], coord[1]),
00952         fill_color_rgba=0x00CC00FFL,
00953         width_units=2.0,
00954         line_style=coord[4]
00955         )
00956       bx = coord[0]
00957       by = coord[1]
00958       ba = coord[2]
00959       one_path.append(coord)
00960 
00961     # --------------------------------------------------------------
00962     # Translate the previous calculation in a string for Tux program
00963     # --------------------------------------------------------------
00964 
00965     ba = 0                              # Boat angle
00966     cumulative_distance = 0
00967     tux_move = ""
00968     for c in one_path:
00969       #print "X,Y,A,D=" + str(c)
00970       x=c[0]
00971       y=c[1]
00972       a=c[2]                            # angle
00973       d=c[3]                            # distance
00974 
00975       if(ba-a<0):
00976         if(cumulative_distance):
00977           tux_move += _("forward") + " " + str(cumulative_distance) + "\n"
00978           cumulative_distance=0
00979         tux_move += _("right") + " " + str(abs(int(ba-a))) + "\n"
00980         ba += abs(int(ba-a))
00981       elif(ba-a>0):
00982         if(cumulative_distance):
00983           tux_move += _("forward") + " " + str(cumulative_distance) + "\n"
00984           cumulative_distance=0
00985         tux_move += _("left") + " " + str(abs(int(ba-a))) + "\n"
00986         ba -= abs(int(ba-a))
00987 
00988       cumulative_distance += int(d/self.sea_ratio)
00989 
00990     # Final move, add an ofset because we loose space in abs()
00991     tux_move += _("forward") + " " + str(cumulative_distance+2) + "\n"
00992 
00993     self.right_boat.tb.set_text(tux_move)
00994     self.right_boat.tv.set_editable(False)
00995 
00996 
00997   # ----------------------------------------
00998   # The RULER
00999   #
01000   def ruler_item_event(self, widget, event=None):
01001     if event.type == gtk.gdk.BUTTON_PRESS:
01002       if event.button == 1:
01003         self.pos_x = event.x
01004         self.pos_y = event.y
01005         self.ruleritem = self.rootitem.add(
01006           gnomecanvas.CanvasLine,
01007           points=( self.pos_x, self.pos_y, event.x, event.y),
01008           fill_color_rgba=0xFF0000FFL,
01009           width_units=2.0
01010           )
01011         return True
01012     if event.type == gtk.gdk.MOTION_NOTIFY:
01013       if event.state & gtk.gdk.BUTTON1_MASK:
01014         # Calc the angle and distance and display them in the status bar
01015         distance = math.sqrt((self.pos_x-event.x)*(self.pos_x-event.x)+(self.pos_y-event.y)*(self.pos_y-event.y))
01016         distance = int(distance/self.sea_ratio)
01017 
01018         angle = math.atan2(abs(self.pos_x-event.x), abs(self.pos_y-event.y))
01019         angle = int(angle*180/math.pi)
01020         angle = abs(angle - 90)
01021         self.statusitem.set(text=_("Distance:") + " " + str(distance) + " " + _("Angle:") + " " + str(angle))
01022         self.ruleritem.set(
01023           points=( self.pos_x, self.pos_y, event.x, event.y)
01024           )
01025     if event.type == gtk.gdk.BUTTON_RELEASE:
01026       if event.button == 1:
01027         self.ruleritem.destroy()
01028         self.statusitem.set(text="")
01029         return True
01030     return False
01031