Script & Config Changes for Editable Maps 9/10/02 Note the following changes and additions to support editable maps and course following boat functionality (based on an 094 distro installation) ---- 0. New property for itemdesc.cfg Map{} entries: Editable, which defaults to 0 This determines if the user can plot points on the map gump. Note you can still edit the map via the script methods listed under item 4. 1. decodeMap.src, CreateTreasureMap: var map := CreateItemInBackpack( who, GetObjtypeByName("map_nonedit"), 1 ); and at the end of SetTreasureCoords: map.AppendPin(x,y); 2. /pkg/skills/cartography/itemdesc.cfg add: Map 0x641E { Name map_nonedit Desc Blank Map Graphic 0x14EB VendorSellsFor 10 VendorBuysFor 5 Editable 0 } all the other entries add: Editable 1 to, except for "rolledmap" and "rolledmap2" 3. /pkg/items/treasuremap/itemdesc.cfg add to all: Editable 0 4. New script methods for map obejcts: GetPins(): returns an array of structs with .x and .y members for each pin point. InsertPin( index, x, y ): inserts a pin with the passed x,y before the passed index. Valid range 0 to n-1 AppendPin( x, y ): appends a pin to the end of the course with the passed x,y ErasePin( index ): erases the pin at the passed index (valid 0 to n-1), and shifts pins down. isa(): returns true if the object is a map object 5. Tillerman course follow functionality: in this boat.src (edit from the 094 distro) //////////////// // boat.src // // control script for boats // // Header replaced by Racalac 9/15/02 // // Todo/Fix: Course nav needs the "goto #" command implimented. // Tillerman string recognition causes boat to pause //////////////// use os; use uo; use util; use boat; use math; include "include/eventID"; include "include/sysEvent"; include "include/myUtil"; include "include/client"; include "include/yesNo"; include "include/math"; include "util/key"; include "plankUtil"; const STATE_STATIONARY := 0; const STATE_MOVING := 1; const STATE_DRIFTING := 2; const STATE_FOLLOWING_COURSE := 3; const DELAY_DRIFTING := 15000; const NEGONE := -1; const ZERO := 0; const POSONE := 1; var state := STATE_STATIONARY; var relative_direction; var ms_delay_between_moves := 200; var boat; var tillerman; var owner := GetObjProperty(boat.hold,"owner"); var decay := GetObjProperty(boat.tillerman, "decay"); var first_move_toward_waypoint := 1; program autostart_boat(param) boat := param; if(!boat) syslog( "[ERROR] [boat.src] Boat script running, but boat not found!" ); return; endif tillerman := boat.tillerman; if(!tillerman) syslog( "[ERROR] [boat.src] No tillerman for boat at: " +(boat.x-1) +","+ boat.y +","+ boat.z + "; destroying the boat..." ); DestroyMulti(boat); return; endif EnableEvents(SYSEVENT_SPEECH, 15); RegisterForSpeechEvents(tillerman, 15); var nextencounter := ReadGameClock()+(RandomInt(120) + 120); var nextsound := ReadGameClock()+5; var driftcounter := 1; //set_critical(1); var x,y; while(boat) if(GetObjProperty(boat.hold, "#speed")) state := STATE_MOVING; ms_delay_between_moves := GetObjProperty(boat.hold, "#speed"); relative_direction := GetObjProperty(boat.hold, "#relativedir"); eraseobjproperty(boat.hold, "#speed"); eraseobjproperty(boat.hold, "#relativedir"); endif decay := getobjproperty(boat.tillerman,"decay"); if((decay) and(!getobjproperty(tillerman,"nodecay"))) if(ReadGameClock() > decay) PrintTextAbove(tillerman, "Arrrrrgh! She's going down, Captain! Abandon ship!"); sleep(4); foreach item in EnumerateItemsInContainer( boat.hold ) DestroyItem(item); endforeach foreach mob in(boat.items) DestroyItem(mob); endforeach foreach mob in(boat.mobiles) MoveCharacterToLocation(mob, 1487, 1869, 0, MOVECHAR_FORCELOCATION ); SendSysMessage(mob, "Your ship, badly in need of maintenance, sinks beneath the waves!"); SendSysMessage(mob, "You awake on a distant shore..."); endforeach if(boat.has_offline_mobiles) foreach mob in(boat.offline_mobiles) MoveCharacterToLocation(mob, 1487, 1869, 0, MOVECHAR_FORCELOCATION ); endforeach endif DestroyMulti(boat); endif endif case(state) STATE_MOVING: if(ReadGameClock() > nextsound) PlayBoatSounds(); nextsound := ReadGameClock()+5; endif x := boat.x; y := boat.y; MoveBoatRelative(boat, relative_direction); if(boat.x = 6 or boat.x = 5097 or boat.y = 6 or boat.y = 4089) WorldWrap(); endif if((x == boat.x) &&(y == boat.y)) ms_delay_between_moves := 1000; state := STATE_DRIFTING; PrintTextAbove(tillerman, "Aaargh! We've stopped!"); //SmackEveryone(); endif sleepms(ms_delay_between_moves); if(ReadGameClock() > nextencounter) DoEncounter(); nextencounter := ReadGameClock()+120; endif while(events_waiting()) process_event(wait_for_event(0)); endwhile STATE_DRIFTING: if(ReadGameClock() > nextsound) PlayBoatSounds(); nextsound := ReadGameClock()+5; endif if(driftcounter > 15) MoveBoatRelative(boat, RandomInt(8)); driftcounter := 1; else driftcounter := driftcounter + 1; endif sleepms(1000); while(events_waiting()) process_event(wait_for_event(0)); endwhile STATE_STATIONARY: var ev := wait_for_event(120); if(ev) process_event(ev); endif STATE_FOLLOWING_COURSE: x := boat.x; y := boat.y; BoatFollowCourse(boat); sleepms(ms_delay_between_moves); while(events_waiting()) process_event(wait_for_event(0)); endwhile endcase if(ReadGameClock() > nextencounter) checkres(); nextencounter := ReadGameClock()+120; endif endwhile endprogram function handle_speech(event) var text := lower(event.text); if(text["drift"] || text["raise anchor"]) ms_delay_between_moves := 1000; state := STATE_DRIFTING; elseif(text["forward"]) if(state = STATE_STATIONARY) PrintTextAbove(tillerman, "The Anchor Is Down Captain!"); else PrintTextAbove(tillerman, "Aye Aye Captain!"); state := STATE_MOVING; if(text["left"]) relative_direction := 7; elseif(text["right"]) relative_direction := 1; else relative_direction := 0; endif endif elseif(text["back"]) if(state = STATE_STATIONARY) PrintTextAbove(tillerman, "The Anchor Is Down Captain!"); else PrintTextAbove(tillerman, "Aye Aye Captain!"); state := STATE_MOVING; if(text["left"]) relative_direction := 5; elseif(text["right"]) relative_direction := 3; else relative_direction := 4; endif endif elseif(text["stop"] || text["furl sail"] || text["furl"]) PrintTextAbove(tillerman, "Aye Aye Captain!"); ms_delay_between_moves := 1000; state := STATE_DRIFTING; elseif(text["drop anchor"]) PrintTextAbove(tillerman, "Aye Aye Captain! Anchor Dropped."); ms_delay_between_moves := 1000; state := STATE_STATIONARY; elseif(text["turn right"] || text["starboard"]) if(state = STATE_STATIONARY) PrintTextAbove(tillerman, "The Anchor Is Down Captain!"); else PrintTextAbove(tillerman, "Aye Aye Captain!"); TurnBoat(boat, 1); endif elseif(text["right"]) if(state = STATE_STATIONARY) PrintTextAbove(tillerman, "The Anchor Is Down Captain!"); else PrintTextAbove(tillerman, "Aye Aye Captain!"); state := STATE_MOVING; relative_direction := 2; endif elseif(text["turn left"] || text["port"]) if(state = STATE_STATIONARY) PrintTextAbove(tillerman, "The Anchor Is Down Captain!"); else PrintTextAbove(tillerman, "Aye Aye Captain!"); TurnBoat(boat, 3); endif elseif(text["left"]) if(state = STATE_STATIONARY) PrintTextAbove(tillerman, "The Anchor Is Down Captain!"); else PrintTextAbove(tillerman, "Aye Aye Captain!"); state := STATE_MOVING; relative_direction := 6; endif elseif(text["come about"] || text["turn around"]) if(state = STATE_STATIONARY) PrintTextAbove(tillerman, "The Anchor Is Down Captain!"); else PrintTextAbove(tillerman, "Aye Aye Captain! Coming About."); TurnBoat(boat, 2); sleep(1); endif /*NEW MAP COURSE COMMANDS*/ elseif(text["set course"]) PrintTextAbove(tillerman, "Select a map for me to follow."); var map := Target(event.source); if(!map.isa(POLCLASS_MAP)) PrintTextAbove(tillerman, "Are ye dense? I need a map!"); else SetObjProperty(tillerman, "course", map.GetPins()); SetObjProperty(tillerman, "nextwaypoint", 1); PrintTextAbove(tillerman, "Arr, course plotted."); endif elseif(text["clear course"]) PrintTextAbove(tillerman, "Arr, course cleared Cap'n."); EraseObjProperty(tillerman, "course"); EraseObjProperty(tillerman, "nextwaypoint"); elseif(text["nav"]) if(state != STATE_FOLLOWING_COURSE) PrintTextAbove(tillerman, "We are not currently following a course."); else PrintTextAbove(tillerman, "We are following our course."); PrintTextAbove(tillerman, "The next waypoint is #" + GetObjProperty(tillerman,"nextwaypoint")); endif elseif(text["start"]) if( (GetObjProperty(tillerman,"course")==error) || (GetObjProperty(tillerman,"nextwaypoint")==error)) PrintTextAbove(tillerman,"Arr, set course first Cap'n."); else state := STATE_FOLLOWING_COURSE; SetObjProperty(tillerman, "nextwaypoint", 1); PrintTextAbove(tillerman,"Aye, sailing to first waypoint."); endif elseif(text["continue"]) if( (GetObjProperty(tillerman,"course")==error) || (GetObjProperty(tillerman,"nextwaypoint")==error)) PrintTextAbove(tillerman,"Arr, set course first Cap'n."); else state := STATE_FOLLOWING_COURSE; PrintTextAbove(tillerman,"Aye, moving on."); endif endif /*END NEW MAP COURSE COMMANDS*/ if(text["full"]) if(state != STATE_STATIONARY) PrintTextAbove(tillerman, "Aye Aye Captain! Full Speed Ahead."); ms_delay_between_moves := 100; endif elseif(text["slow"]) if(state != STATE_STATIONARY) PrintTextAbove(tillerman, "Aye Aye Captain! "); ms_delay_between_moves := 1000; endif endif sleep(1); if(text["one"]) if(state != STATE_STATIONARY) MoveBoatRelative(boat, relative_direction); state := STATE_STATIONARY; endif endif endfunction function process_event(event) if(event.type == SYSEVENT_SPEECH) if(CanCommandMe(event.source)) var text := lower(event.text); if(text["drydock"]) PrintTextAbove(tillerman, "Aye Aye Captain! Docking."); drydock(event); sleep(1); elseif(text["status"]) decay := GetObjProperty(boat.tillerman, "decay"); if((decay - ReadGameClock() ) > 861000 ) PrintTextAbove( tillerman, "Arrh, the ship's in excellent shape! She's a fine vessel indeed, Captain!" ); elseif((decay - ReadGameClock() ) > 604800 ) PrintTextAbove( tillerman, "She's slightly worn, Captain."); elseif((decay - ReadGameClock() ) > 259200 ) PrintTextAbove(tillerman, "She's fairly worn, Captain."); elseif((decay - ReadGameClock() ) > 86400 ) PrintTextAbove(tillerman, "Arrr, she's greatly worn and in dire need of repair, Captain!"); else PrintTextAbove(tillerman, "Arrr, she's going to sink within the day if she don't get repairs soon, Captain!"); endif else handle_speech(event); endif endif endif endfunction function CanCommandMe(who) owner := GetObjProperty(boat.tillerman, "owner"); if(who.serial == owner) return 1; else var packkey; var lokid := GetObjProperty(boat.tillerman, "lockid"); foreach thing in EnumerateItemsInContainer(who.backpack) if(GetObjProperty(thing, "lockid") = lokid) packkey := 1; break; endif endforeach if(packkey = 1) return 1; else return 0; endif endif endfunction function DoEncounter() foreach who in ListMobilesNearLocationEx(boat.x, boat.y, GetMapInfo(boat.x, boat.y).z, 4, LISTEX_FLAG_GHOST) if(YesNo(who,"Ressurect?")) PlaySoundEffect(who, SFX_SPELL_RESSURECTION); PlaySoundEffect(who, SFX_SPELL_RESSURECTION); Resurrect(who); endif endforeach var who :=(boat.mobiles); who := who[1]; if(!who) return; endif var x; var y; var z; x := RandomInt(15)-5; y := RandomInt(15)-5; x := x + boat.x; y := y + boat.y; if(x > boat.x) x := x + 5; else x := x - 5; endif if(y > boat.y) y := y + 5; else y := y - 5; endif z := GetMapInfo(x, y).z; if(z >= who.z) return; endif var it := CreateNpcFromTemplate(getcritter(), x, y, z, 0); if(it) SetObjProperty(it,"killme",1); endif var ev := {}; ev.+type := EVID_ENTEREDAREA; ev.+source := who; SendEvent(it, ev); endfunction function checkres() foreach who in ListMobilesNearLocationEx(boat.x, boat.y, GetMapInfo(boat.x, boat.y).z, 4, LISTEX_FLAG_GHOST) if(who in boat.mobiles) if(YesNo(who,"Resurrect?")) PlaySoundEffect(who, SFX_SPELL_RESSURECTION); PlaySoundEffect(who, SFX_SPELL_RESSURECTION); Resurrect(who); endif endif endforeach endfunction function getcritter() case(RandomInt(9)) 0: return "walrus"; 1: return "walrus"; 2: return "walrus"; 3: return "walrus"; 4: return "alligator"; 5: return "alligator"; 6: return "waterelemental"; 7: return "seaserpent"; 8: return "airelemental"; endcase endfunction function drydock(event) if(GetObjProperty(event.source, "#DryDocking")) SendSysMessage(event.source, "You are already doing something else."); return; endif SetObjProperty(event.source, "#DryDocking", 1); EraseObjProperty(event.source, "#DryDocking"); var text := lower(event.text); if(!text["drydock"]) EraseObjProperty(event.source, "#DryDocking"); return; endif var me := event.source; var items := boat.items; if(len(items) > 0) PrintTextAbovePrivate(boat.tillerman, "You cannot drydock the boat while items are on deck.", me); EraseObjProperty(me, "#DryDocking"); return; endif items := EnumerateItemsInContainer(boat.hold); if(len(items) > 0) PrintTextAbovePrivate(boat.tillerman, "You cannot drydock the boat while items are in the hold.", me); EraseObjProperty(me, "#DryDocking"); return; endif if(CanDockMe(boat, me)) if(!YesNo(me,"Drydock?")) EraseObjProperty(me, "#DryDocking"); return; endif var shiptype; case(boat.objtype) 0x6040: shiptype := 0x6027; 0x6041: shiptype := 0x6028; 0x6042: shiptype := 0x6029; 0x6043: shiptype := 0x602a; 0x6044: shiptype := 0x602b; 0x6045: shiptype := 0x602c; endcase if(!shiptype) SendSysMessage(me, "Your boat seems to be malfunctioning. Please page a GM."); return; endif var newboat := CreateItemInContainer(me.backpack, shiptype, 1); var key := FindKey(me, boat.tillerman); if(key) DestroyItem(key); endif if(!newboat) SendSysMessage(me,"Could not create boat deed in your backpack!"); EraseObjProperty(me, "#DryDocking"); return; endif if(ReserveItem(newboat)) newboat.graphic := 0x14f3; if(!boat.tillerman.name) newboat.name := "a toy boat"; else newboat.name := boat.tillerman.name; endif if(!DestroyBoat(boat)) DestroyItem(newboat); endif else DestroyItem(newboat); endif endif EraseObjProperty(me, "#DryDocking"); endfunction function DestroyBoat(who) var res := DestroyMulti(boat); if(!res) return 0; endif return 1; endfunction function SmackEveryone() foreach mob in(boat.mobiles) ApplyDamage(mob,RandomInt(10)); PerformAction(mob,0x14); PlaySoundEffect(mob,0x110); endforeach endfunction function PlayBoatSounds() var who := RandomInt(len(boat.mobiles)+1); var mobs :=(boat.mobiles); if(RandomInt(2) == 1) PlaySoundEffect(mobs[who],0x13); else PlaySoundEffect(mobs[who],0x14); endif endfunction function WorldWrap() var newx := boat.x; var newy := boat.y; if(boat.y <= 6) newy := 4088; elseif(boat.y >= 4089) newy := 6; endif if(boat.x <= 6) newx := 5096; elseif(boat.x >= 5097) newx := 6; endif var lockid := GetObjProperty(boat.hold, "lockid"); var owner := GetObjProperty(boat.hold, "owner"); var shiptype := boat.objtype; var shipfacing, created; case(tillerman.graphic) 0x3e4e: shipfacing := CRMULTI_FACING_NORTH; 0x3e55: shipfacing := CRMULTI_FACING_EAST; 0x3e4b: shipfacing := CRMULTI_FACING_SOUTH; 0x3e50: shipfacing := CRMULTI_FACING_WEST; default: shipfacing := "Error!"; endcase if(shipfacing="Error!") syslog( "[ERROR] [boat.src] Couldn't tell which way the ship was facing for the worldwrap!"); endif created := CreateMultiAtLocation( newx, newy, -5, shiptype, shipfacing); if(!created) syslog( "[ERROR] [boat.src] New ship couldn't be created for worldwrap!"); return; endif var oldshiphold := boat.hold; var newshiphold := created.hold; foreach item in EnumerateItemsInContainer( oldshiphold ) if(item.container = oldshiphold) MoveItemToContainer(item, newshiphold); endif endforeach foreach item in EnumerateItemsInContainer( oldshiphold ) MoveItemToContainer(item, newshiphold); endforeach foreach mob in(boat.mobiles) MoveCharacterToLocation(mob,(mob.x-boat.x)+created.x,(mob.y-boat.y)+created.y, -2, MOVECHAR_FORCELOCATION ); endforeach if(boat.has_offline_mobiles) foreach mob in(boat.offline_mobiles) MoveCharacterToLocation(mob,(mob.x-boat.x)+created.x,(mob.y-boat.y)+created.y, -2, MOVECHAR_FORCELOCATION ); endforeach endif foreach mob in(boat.items) MoveitemToLocation(mob,(mob.x-boat.x)+created.x,(mob.y-boat.y)+created.y, -2,0 ); endforeach var newtillerman := created.tillerman; newtillerman.name := tillerman.name; SetObjProperty( created.starboardplank, "lockid", lockid); SetObjProperty( created.portplank, "lockid", lockid); SetObjProperty( created.ship.starboardplank, "owner", owner ); SetObjProperty( created.ship.portplank, "owner", owner ); SetObjProperty( created.ship.starboardplank, "tillermanid", newtillerman.serial ); SetObjProperty( created.ship.portplank, "tillermanid", newtillerman.serial ); SetObjProperty( created.hold, "lockid", lockid); SetObjProperty( created.hold, "owner", owner); SetObjProperty( created.tillerman, "owner", owner); SetObjProperty( created.tillerman, "shipserial", created.serial); SetObjProperty( created.tillerman, "lockid", lockid); SetObjProperty( created.tillerman, "decay", decay); if(boat.starboardplank.locked = 1) created.starboardplank.locked := 1; endif if(boat.portplank.locked = 1) created.portplank.locked := 1; endif if(boat.hold.locked = 1) created.hold.locked := 1; endif SetObjProperty( created.hold, "#relativedir", relative_direction); SetObjProperty( created.hold, "#speed", ms_delay_between_moves); created.tillerman.usescript := ":boat:tillerman"; if(!DestroyMulti(boat)) DestroyItem(boat.tillerman); syslog( "[ERROR] [boat.src] Old ship at " +(boat.x-1) +","+ boat.y +","+ boat.z + " couldn't be destroyed during worldwrap!"); endif endfunction function CanDockMe(boat, who) if(GetObjProperty(boat.hold,"owner") == who.serial) return 1; else var packkey; var lokid := GetObjProperty(boat.tillerman, "lockid"); foreach thing in EnumerateItemsInContainer(who.backpack) if(GetObjProperty(thing, "lockid") = lokid) packkey := 1; break; endif endforeach if(packkey = 1) return 1; else return 0; endif endif endfunction /*NEW MAP COURSE FUNCTIONS*/ //follows the current course function BoatFollowCourse(boat) var startx, starty, destx, desty; var course, nextwaypoint; var newpoint, newfacing, prevfacing; course := GetObjProperty(boat.tillerman,"course"); nextwaypoint := GetObjProperty(boat.tillerman,"nextwaypoint"); startx := boat.x; starty := boat.y; prevfacing := boat.facing; if( (course==error) || (nextwaypoint==error)) return; endif destx := course[nextwaypoint].x; desty := course[nextwaypoint].y; newpoint := lineBresenham(startx,starty,destx,desty); newfacing := GetNewFacing(startx,starty,newpoint[1],newpoint[2]); if(newfacing == error) //reached waypoint/end nextwaypoint := nextwaypoint + 1; first_move_toward_waypoint := 1; //so we turn toward next waypoint (includes next course follow if we're done with this one) if(nextwaypoint > course.size()) //done state := STATE_DRIFTING; PrintTextAbove(boat.tillerman,"Arr, we've reached our destination."); return; else SetObjProperty(boat.tillerman,"nextwaypoint",nextwaypoint); endif else if(first_move_toward_waypoint == 1) if( prevfacing != newfacing ) TurnBoat(boat,Cint(DetermineTurnCode(boat,newfacing)) ); endif first_move_toward_waypoint := 0; endif MoveBoat(boat,newfacing); if((startx == boat.x) &&(starty == boat.y)) //we didn't move. must be blocked state := STATE_DRIFTING; PrintTextAbove(tillerman, "Aaargh! We've stopped!"); endif endif endfunction //convert facing 0-7 to turn code 0-3 function DetermineTurnCode( boat, desired ) var current := CInt(boat.facing / 2); desired := CInt(desired / 2); if ( desired < current ) desired := desired + 4; endif return (desired - current); endfunction //returns the direction (x1,y1) lies from (x0,y0) in the range 0-7. // returns error if the 2 points are equal. function GetNewFacing(x0,y0,x1,y1) var dx, dy; var facing; dx := x1 - x0; dy := y1 - y0; case(dx) NEGONE: case(dy) NEGONE: facing := 7; ZERO: facing := 6; POSONE: facing := 5; default: facing := error; endcase ZERO: case(dy) NEGONE: facing := 0; ZERO: facing := error; POSONE: facing := 4; default: facing := error; endcase POSONE: case(dy) NEGONE: facing := 1; ZERO: facing := 2; POSONE: facing := 3; default: facing := error; endcase default: facing := error; endcase return facing; endfunction //Bresenham's line drawing algorithm. determines a line between two waypoints. function lineBresenham(x0, y0, x1, y1) var dy; dy := y1 - y0; var dx; dx := x1 - x0; var stepx, stepy; if (dy < 0) dy := -dy; stepy := -1; else stepy := 1; endif if (dx < 0) dx := -dx; stepx := -1; else stepx := 1; endif dy := 2*dy; dx := 2*dx; if (dx > dy) var fraction; fraction := 2*dy - dx; while (x0 != x1) if (fraction >= 0) y0 := y0 + stepy; fraction := fraction - dx; endif x0 := x0 + stepx; fraction := fraction + dy; return {x0, y0}; endwhile else var fraction; fraction := 2*dx - dy; while (y0 != y1) if (fraction >= 0) x0 := x0 + stepx; fraction := fraction - dy; endif y0 := y0 + stepy; fraction := fraction + dx; return {x0, y0}; endwhile endif endfunction /*END NEW MAP COURSE FUNCTIONS*/