Addition of Monster Race with reward system.

Adapted from aCis free version.
This commit is contained in:
MobiusDevelopment
2020-02-02 04:36:04 +00:00
parent 92ac8e59a7
commit 20a4e99da1
333 changed files with 22197 additions and 10208 deletions

View File

@@ -0,0 +1,15 @@
CREATE TABLE IF NOT EXISTS `mdt_bets` (
`lane_id` INT(1) DEFAULT 0,
`bet` INT DEFAULT 0,
PRIMARY KEY (`lane_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;
INSERT INTO `mdt_bets` VALUES
('1','0'),
('2','0'),
('3','0'),
('4','0'),
('5','0'),
('6','0'),
('7','0'),
('8','0');

View File

@@ -0,0 +1,7 @@
CREATE TABLE IF NOT EXISTS `mdt_history` (
`race_id` MEDIUMINT DEFAULT 0,
`first` INT(1) DEFAULT 0,
`second` INT(1) DEFAULT 0,
`odd_rate` DOUBLE(10,2) DEFAULT 0,
PRIMARY KEY (`race_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;

View File

@@ -416,9 +416,6 @@
<admin command="admin_mobgroup_attackgrp" accessLevel="100" /> <admin command="admin_mobgroup_attackgrp" accessLevel="100" />
<admin command="admin_mobgroup_invul" accessLevel="100" /> <admin command="admin_mobgroup_invul" accessLevel="100" />
<!-- ADMIN MONSTER RACE -->
<admin command="admin_mons" accessLevel="100" />
<!-- ADMIN ONLINE --> <!-- ADMIN ONLINE -->
<admin command="admin_online" accessLevel="100" /> <admin command="admin_online" accessLevel="100" />

View File

@@ -1,19 +1,22 @@
<html><body><br><br> <html><body>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 0">Back</Button><br> <br>
In a monster race eight monsters race at the same time. If you guess which monster is expected to finish first and buy that monster's race ticket, based upon the odds to win you will receive an appropriate amount of Adena.<br> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 0">Go Back</Button>
<font color="LEVEL">How the Race Works</font>Monster races run every 20 minutes in real time.<br> <br>
<font color="LEVEL">How to Participate</font><br> In a monster race eight monsters race at the same time. If you guess which monster is expected to finish first and buy that monster's race ticket, based upon the odds to win you will receive an appropriate amount of adena.<br>
You can purchase a monster race ticket through a race manager NPC. Only between 18 minutes before the start of a race until 3 minutes before the race begins can you buy a ticket for the applicable race. When you purchase a ticket through a race manager, you can find out the names of the participating monsters and their current statistics. Three minutes before a race starts, the race manager will stop ticket sales and announce the monsters' individual odds for the current race.<br> <font color="LEVEL">Racing Schedule</font><br>
<font color="LEVEL">Betting</font><br> Monster races every 20 minutes in real time.<br>
There are two types of race betting. The Win bet means that you back a particular monster to win 1st place. The Place bet means that you will have to guess the 1st and 2nd place monsters, regardless of who finishes first or second.<br> <font color="LEVEL">Method of Race Participation</font><br>
You can purchase a monster race ticket through a race manager NPC. Only between 18 minutes before the start of a race until 3 minutes before the race begins you can buy a ticket for the applicable race. When you purchase a ticket through a conversation with a race manager, you can find out the names of participating monsters in the race and their current statistics. Five minutes before a race starts, the race manager will stop ticket sales and announce monsters' individual odds rate for the current race.<br>
<font color="LEVEL">Types of Race</font><br>
There are two types of race betting. The Win type betting means that you back a monster that will win the first prize. Place type of betting means that you will have to guess the 1st and 2nd winning monsters, regardless of who finishes first or second.<br>
<font color="00FFFF">Win</font><br1> <font color="00FFFF">Win</font><br1>
* Among the entire betting amount for Win betting, after subtracting the race track's own share of profit, the amount left will be your total winnings. Three minutes before the race, each monster's expected odds are announced. Each Win ticket's dividend amount is calculated by multiplying the odds by the price of a ticket. The odds are never less than 1.<br1> * Among the entire betting amount for Win betting, after subtracting the race track's own share of profit, the amount left will be your total winnings. Three minutes before the race each monster's expected odds are announced. Each Win ticket's dividend amount is calculated by multiplying the odds by the price of a ticket. The odds rate is never less than 1.<br1>
* The prize money does not get transferred to the next race.<br> * The prize money does not get transferred to the next race.<br>
<font color="00FFFF">Place</font><br1> <font color="00FFFF">Place</font><br1>
* Place betting is a way of betting when you correctly guess the two monsters who will finish first and second regardless of their order. The method of calculating the dividend amount is the same as Win betting.<br1> * Place betting is a way of betting when you correctly guess the two monsters who will finish first and second regardless of their order. The method of calculating dividend amount is the same as Win betting.<br>
* Currently place betting is not available.<br> * Currently place betting is not offered.<br>
<font color="LEVEL">End of Race</font><br> <font color="LEVEL">End of Race</font><br>
When a race is over, the race manager will shout the results of the race. You can trade your ticket for Adena by speaking to a race manager NPC. Check the Winning Information Data to find out the types of bets and total winnings for the race you bet on, as well as your win status and prize amount. If you won, you can receive your prize money thorough a race manager NPC. If you did not win, a race manager will buy back your ticket for a small amount of money.<br> When a race is over, the race manager will shout out the results of the race. Through a conversation with a race manager NPC, you can trade your ticket for adena. By looking up on the Winning Information Data, you can find out the types of tickets and quantity of the winnings in the race you betted on and your win status and prize amount you won. If you won, you can receive your prize money thorough a race manager NPC. If you did not win, a race manager will buy back your ticket by paying you a very small amount of money.<br>
If a server goes down while tickets are being sold, or while a race is in progress, the race will be cancelled. You can receive a full refund for your ticket for a cancelled race through a race manager.<br> If a server goes down while tickets are being sold, or while a race is in progress, the race will be cancelled. You can receive a full refund for your ticket for a cancelled race through a race manager.<br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 0">Back</Button> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 0">Go Back</Button>
</body></html> </body></html>

View File

@@ -3,6 +3,7 @@
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=1 height=3> <img src="L2UI.SquareBlank" width=1 height=3>
</center> </center>
<table width=270 border=0 bgcolor="111111"> <table width=270 border=0 bgcolor="111111">
<tr> <tr>
<td fixwidth=60>Lane Selection</td> <td fixwidth=60>Lane Selection</td>
@@ -11,6 +12,7 @@
<td fixwidth=30 align=right>Odds</td> <td fixwidth=30 align=right>Odds</td>
</tr> </tr>
</table> </table>
<table width=270 border=0> <table width=270 border=0>
<tr> <tr>
<td fixwidth=13><img src="L2UI.rightBtn2" width=14 height=13></td> <td fixwidth=13><img src="L2UI.rightBtn2" width=14 height=13></td>
@@ -20,18 +22,21 @@
<td fixwidth=30 align=right><font color="00ffff">13%</font></td> <td fixwidth=30 align=right><font color="00ffff">13%</font></td>
</tr> </tr>
</table> </table>
<table width=270 border=0> <table width=270 border=0>
<tr><td width=100>Select Bet Amount</td> <tr><td width=100>Select Bet Amount</td>
<td width=170> <td width=170>
<a action="bypass -h npc_%objectId%_BuyTicket 11" >100 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 12" >500 Adena</a><br1> <a action="bypass -h npc_%objectId%_BuyTicket 11" >100 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 12" >500 Adena</a> <br1>
<a action="bypass -h npc_%objectId%_BuyTicket 13" >1,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 14" >5,000 Adena</a><br1> <a action="bypass -h npc_%objectId%_BuyTicket 13" >1,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 14" >5,000 Adena</a> <br1>
<a action="bypass -h npc_%objectId%_BuyTicket 15" >10,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 16" >20,000 Adena</a><br1> <a action="bypass -h npc_%objectId%_BuyTicket 15" >10,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 16" >20,000 Adena</a> <br1>
<a action="bypass -h npc_%objectId%_BuyTicket 17" >50,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 18" >100,000 Adena</a><br1> <a action="bypass -h npc_%objectId%_BuyTicket 17" >50,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 18" >100,000 Adena</a> <br1>
</td></tr> </td></tr>
</table> </table>
<img src="L2UI.SquareBlank" width=270 height=10> <img src="L2UI.SquareBlank" width=270 height=10>
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=270 height=5> <img src="L2UI.SquareBlank" width=270 height=5>
<table width=270 border=0 cellpadding=0 cellspacing=0> <table width=270 border=0 cellpadding=0 cellspacing=0>
<tr> <tr>
<td width=70 height=20><button value="Select Lane" action="bypass -h npc_%objectId%_BuyTicket 0" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td> <td width=70 height=20><button value="Select Lane" action="bypass -h npc_%objectId%_BuyTicket 0" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td>
@@ -39,4 +44,5 @@
<td width=70 align=right><button value="Confirm" action="bypass -h npc_%objectId%_BuyTicket 20" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td> <td width=70 align=right><button value="Confirm" action="bypass -h npc_%objectId%_BuyTicket 20" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td>
</tr> </tr>
</table> </table>
</body></html> </body></html>

View File

@@ -11,6 +11,7 @@
<td fixwidth=30 align=right>Odds</td> <td fixwidth=30 align=right>Odds</td>
</tr> </tr>
</table> </table>
<table width=270 border=0> <table width=270 border=0>
<tr> <tr>
<td fixwidth=13><img src="L2UI.rightBtn2" width=14 height=13></td> <td fixwidth=13><img src="L2UI.rightBtn2" width=14 height=13></td>
@@ -20,6 +21,7 @@
<td fixwidth=30 align=right><font color="00ffff">13%</font></td> <td fixwidth=30 align=right><font color="00ffff">13%</font></td>
</tr> </tr>
</table> </table>
<table width=270 border=0 bgcolor=111111> <table width=270 border=0 bgcolor=111111>
<tr> <tr>
<td fixwidth=60>Bet Amount </td> <td fixwidth=60>Bet Amount </td>
@@ -37,11 +39,13 @@
<td>Total </td> <td>Total </td>
<td><font color="LEVEL">0total</font> Adena</td> <td><font color="LEVEL">0total</font> Adena</td>
</tr> </tr>
</table><br> </table>
<br>
<center>If the information is correct, please click Confirm.</center><br> <center>If the information is correct, please click Confirm.</center><br>
<img src="L2UI.SquareBlank" width=270 height=10> <img src="L2UI.SquareBlank" width=270 height=10>
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=270 height=5> <img src="L2UI.SquareBlank" width=270 height=5>
<table width=270 border=0 cellpadding=0 cellspacing=0> <table width=270 border=0 cellpadding=0 cellspacing=0>
<tr> <tr>
<td width=70 height=20><button value=&$141; action="bypass -h npc_%objectId%_Chat 0" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td> <td width=70 height=20><button value=&$141; action="bypass -h npc_%objectId%_Chat 0" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td>

View File

@@ -15,49 +15,49 @@
<tr> <tr>
<td fixwidth=40>No.1</td> <td fixwidth=40>No.1</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob1</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob1</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd1</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.2</td> <td fixwidth=40>No.2</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob2</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob2</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd2</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.3</td> <td fixwidth=40>No.3</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob3</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob3</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd3</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.4</td> <td fixwidth=40>No.4</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob4</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob4</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd4</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.5</td> <td fixwidth=40>No.5</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob5</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob5</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd5</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.6</td> <td fixwidth=40>No.6</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob6</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob6</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd6</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.7</td> <td fixwidth=40>No.7</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob7</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob7</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd7</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.8</td> <td fixwidth=40>No.8</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob8</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob8</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd8</font> </td>
</tr> </tr>
</table> </table>
<img src="L2UI.SquareBlank" width=270 height=10> <img src="L2UI.SquareBlank" width=270 height=10>

View File

@@ -3,22 +3,12 @@
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=1 height=3> <img src="L2UI.SquareBlank" width=1 height=3>
<table width=270 border=0 bgcolor=111111> <table width=270 border=0 bgcolor=111111>
<tr><td fixwidth=60>Winnings for a Race.</td><td fixwidth=60 align=right>Lane</td><td fixwidth=160 align=right>Bet Amount</td></tr> <tr><td fixwidth=70>Winnings for a Race.</td><td fixwidth=70 align=right>Lane</td><td fixwidth=130 align=right>Bet Amount</td></tr>
<tr><td fixwidth=60 fixheight=1></td><td fixwidth=60></td><td fixwidth=160></td></tr> <tr><td fixwidth=70 fixheight=1></td><td fixwidth=70></td><td fixwidth=130></td></tr>
</table> </table>
<table width=270 border=0> <table width=270 border=0>
<tr> %tickets%
<td><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 8">1 Race Number</Button></td>
<td align=right><font color="LEVEL">1</font> Number</td>
<td align=right><font color="LEVEL">100</font> Adena</td>
</tr>
</table> </table>
<img src="L2UI.SquareWhite" width=270 height=1>
<table width=270 border=0>
<tr><td width=90><Button ALIGN=LEFT ICON="NORMAL" action="bypass _mrsl01_1">Previous List</Button></td><td width=90 align=center>1 Page </td><td width=90 align=right><Button ALIGN=LEFT ICON="NORMAL" action="bypass _mrsl01_2"> Next List</Button></td></tr>
</table>
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=270 height=5> <img src="L2UI.SquareBlank" width=270 height=5>
<table width=270 border=0 cellpadding=0 cellspacing=0> <table width=270 border=0 cellpadding=0 cellspacing=0>

View File

@@ -4,11 +4,11 @@
<img src="L2UI.SquareBlank" width=1 height=3> <img src="L2UI.SquareBlank" width=1 height=3>
<table width=270 border=0> <table width=270 border=0>
<tr> <tr>
<td width=230>Race #19090<br> <td width=230>Race #%raceId%<br>
Lane 1<br> Lane: %lane%<br>
Bet: 100 adena<br> Bet: %bet% Adena<br>
1st place: Lane [4]<br> 1st place: Lane [%firstLane%]<br>
Odds:[0.01] Odds: [%odd%]
</td> </td>
</tr> </tr>
</table> </table>
@@ -18,8 +18,8 @@ Odds:[0.01]
<table width=270 border=0 cellpadding=0 cellspacing=0> <table width=270 border=0 cellpadding=0 cellspacing=0>
<tr> <tr>
<td width=90 height=20><button value="Previous" action="bypass -h npc_%objectId%_Chat 7" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td> <td width=90 height=20><button value="Previous" action="bypass -h npc_%objectId%_ShowTickets" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td>
<td width=90></td> <td width=90></td>
<td width=90 align=right><button value="Calculate" action="bypass _mrsl03_30635184" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td> <td width=90 align=right><button value="Calculate" action="bypass -h npc_%objectId%_CalculateWin %ticketObjectId%" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td>
</tr></table> </tr></table>
</body></html> </body></html>

View File

@@ -7,41 +7,7 @@
</table> </table>
<table width=270 border=0> <table width=270 border=0>
<tr><td fixwidth=60 fixheight=1></td><td fixwidth=70></td><td fixwidth=70></td><td fixwidth=70></td></tr> <tr><td fixwidth=60 fixheight=1></td><td fixwidth=70></td><td fixwidth=70></td><td fixwidth=70></td></tr>
<tr> %infos%
<td><font color="LEVEL">7</font> th</td>
<td><font color="LEVEL">8</font> Lane </td><td><font color="LEVEL">1</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">6</font> th</td>
<td><font color="LEVEL">4</font> Lane </td><td><font color="LEVEL">8</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">5</font> th</td>
<td><font color="LEVEL">7</font> Lane </td><td><font color="LEVEL">2</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">4</font> th</td>
<td><font color="LEVEL">6</font> Lane </td><td><font color="LEVEL">8</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">3</font> th</td>
<td><font color="LEVEL">4</font> Lane </td><td><font color="LEVEL">1</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">2</font> th</td>
<td><font color="LEVEL">1</font> Lane </td><td><font color="LEVEL">8</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">1</font> th</td>
<td><font color="LEVEL">7</font> Lane </td><td><font color="LEVEL">6</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
</table> </table>
<img src="L2UI.SquareBlank" width=270 height=10> <img src="L2UI.SquareBlank" width=270 height=10>
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>

View File

@@ -1,15 +1,15 @@
<html>
<body>
<center>Monster Race Manager</center> <center>Monster Race Manager</center>
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareBlank" width=1 height=3> <img src="L2UI.SquareWhite" width=270 height=1>
<table width=270 border=0> <img src="L2UI.SquareBlank" width=1 height=3>
<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 1">Monster Race Help</Button></td></tr> <br>
<!--<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_ShowOdds">View Odds</Button></td></tr>--> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 1">Monster Race Help</Button>
<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_ShowInfo">View Monster Information</Button></td></tr> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_ShowOdds">View Odds</Button>
<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_BuyTicket 0">Purchase Ticket</Button></td></tr> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_ShowInfo">View Monster Information</Button>
<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 7">Calculate Winnings</Button></td></tr> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_BuyTicket 0">Purchase Ticket</Button>
<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 9">View Past Results</Button></td></tr> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_ShowTickets">Calculate Winnings</Button>
<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Quest TeleportToRaceTrack" msg="1298;Monster Race Track">Exit the Monster Race Track</Button></td></tr> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_ViewHistory">View Past Results</Button>
</table> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Quest TeleportToRaceTrack" msg="1298;Monster Race Track">Exit the monster race track.</Button>
<img src="L2UI.SquareBlank" width=270 height=10> <img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareBlank" width=270 height=5> <img src="L2UI.SquareBlank" width=270 height=10>
<img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=270 height=5>
</body></html> </body></html>

View File

@@ -105,7 +105,6 @@ import handlers.admincommandhandlers.AdminMenu;
import handlers.admincommandhandlers.AdminMessages; import handlers.admincommandhandlers.AdminMessages;
import handlers.admincommandhandlers.AdminMissingHtmls; import handlers.admincommandhandlers.AdminMissingHtmls;
import handlers.admincommandhandlers.AdminMobGroup; import handlers.admincommandhandlers.AdminMobGroup;
import handlers.admincommandhandlers.AdminMonsterRace;
import handlers.admincommandhandlers.AdminOlympiad; import handlers.admincommandhandlers.AdminOlympiad;
import handlers.admincommandhandlers.AdminOnline; import handlers.admincommandhandlers.AdminOnline;
import handlers.admincommandhandlers.AdminPForge; import handlers.admincommandhandlers.AdminPForge;
@@ -440,7 +439,6 @@ public class MasterHandler
AdminMessages.class, AdminMessages.class,
AdminMissingHtmls.class, AdminMissingHtmls.class,
AdminMobGroup.class, AdminMobGroup.class,
AdminMonsterRace.class,
AdminOlympiad.class, AdminOlympiad.class,
AdminOnline.class, AdminOnline.class,
AdminPathNode.class, AdminPathNode.class,

View File

@@ -1,166 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package handlers.admincommandhandlers;
import org.l2jmobius.commons.concurrent.ThreadPool;
import org.l2jmobius.gameserver.MonsterRace;
import org.l2jmobius.gameserver.handler.IAdminCommandHandler;
import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
import org.l2jmobius.gameserver.network.SystemMessageId;
import org.l2jmobius.gameserver.network.serverpackets.DeleteObject;
import org.l2jmobius.gameserver.network.serverpackets.MonRaceInfo;
import org.l2jmobius.gameserver.network.serverpackets.PlaySound;
import org.l2jmobius.gameserver.network.serverpackets.SystemMessage;
/**
* This class handles following admin commands: - invul = turns invulnerability on/off
* @version $Revision: 1.1.6.4 $ $Date: 2007/07/31 10:06:00 $
*/
public class AdminMonsterRace implements IAdminCommandHandler
{
private static final String[] ADMIN_COMMANDS =
{
"admin_mons"
};
static int state = -1;
@Override
public boolean useAdminCommand(String command, PlayerInstance activeChar)
{
if (command.equalsIgnoreCase("admin_mons"))
{
handleSendPacket(activeChar);
}
return true;
}
@Override
public String[] getAdminCommandList()
{
return ADMIN_COMMANDS;
}
private void handleSendPacket(PlayerInstance activeChar)
{
/*
* -1 0 to initialize the race 0 15322 to start race 13765 -1 in middle of race -1 0 to end the race 8003 to 8027
*/
final int[][] codes =
{
{
-1,
0
},
{
0,
15322
},
{
13765,
-1
},
{
-1,
0
}
};
final MonsterRace race = MonsterRace.getInstance();
if (state == -1)
{
state++;
race.newRace();
race.newSpeeds();
final MonRaceInfo spk = new MonRaceInfo(codes[state][0], codes[state][1], race.getMonsters(), race.getSpeeds());
activeChar.sendPacket(spk);
activeChar.broadcastPacket(spk);
}
else if (state == 0)
{
state++;
final SystemMessage sm = new SystemMessage(SystemMessageId.THEY_RE_OFF);
sm.addInt(0);
activeChar.sendPacket(sm);
final PlaySound sRace = new PlaySound(1, "S_Race", 0, 0, 0, 0, 0);
activeChar.sendPacket(sRace);
activeChar.broadcastPacket(sRace);
final PlaySound sRace2 = new PlaySound(0, "ItemSound2.race_start", 1, 121209259, 12125, 182487, -3559);
activeChar.sendPacket(sRace2);
activeChar.broadcastPacket(sRace2);
final MonRaceInfo spk = new MonRaceInfo(codes[state][0], codes[state][1], race.getMonsters(), race.getSpeeds());
activeChar.sendPacket(spk);
activeChar.broadcastPacket(spk);
ThreadPool.schedule(new RunRace(codes, activeChar), 5000);
}
}
private class RunRace implements Runnable
{
private final int[][] codes;
private final PlayerInstance activeChar;
public RunRace(int[][] pCodes, PlayerInstance pActiveChar)
{
codes = pCodes;
activeChar = pActiveChar;
}
@Override
public void run()
{
// int[][] speeds1 = MonsterRace.getInstance().getSpeeds();
// MonsterRace.getInstance().newSpeeds();
// int[][] speeds2 = MonsterRace.getInstance().getSpeeds();
/*
* int[] speed = new int[8]; for (int i=0; i<8; i++) { for (int j=0; j<20; j++) { //LOGGER.info("Adding "+speeds1[i][j] +" and "+ speeds2[i][j]); speed[i] += (speeds1[i][j]*1); // + (speeds2[i][j]*1); } LOGGER.info("Total speed for "+(i+1)+" = "+speed[i]); }
*/
final MonRaceInfo spk = new MonRaceInfo(codes[2][0], codes[2][1], MonsterRace.getInstance().getMonsters(), MonsterRace.getInstance().getSpeeds());
activeChar.sendPacket(spk);
activeChar.broadcastPacket(spk);
ThreadPool.schedule(new RunEnd(activeChar), 30000);
}
}
private static class RunEnd implements Runnable
{
private final PlayerInstance activeChar;
public RunEnd(PlayerInstance pActiveChar)
{
activeChar = pActiveChar;
}
@Override
public void run()
{
DeleteObject obj = null;
for (int i = 0; i < 8; i++)
{
obj = new DeleteObject(MonsterRace.getInstance().getMonsters()[i]);
activeChar.sendPacket(obj);
activeChar.broadcastPacket(obj);
}
state = -1;
}
}
}

View File

@@ -140,6 +140,7 @@ import org.l2jmobius.gameserver.instancemanager.SiegeGuardManager;
import org.l2jmobius.gameserver.instancemanager.SiegeManager; import org.l2jmobius.gameserver.instancemanager.SiegeManager;
import org.l2jmobius.gameserver.instancemanager.WalkingManager; import org.l2jmobius.gameserver.instancemanager.WalkingManager;
import org.l2jmobius.gameserver.instancemanager.ZoneManager; import org.l2jmobius.gameserver.instancemanager.ZoneManager;
import org.l2jmobius.gameserver.instancemanager.games.MonsterRace;
import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.World;
import org.l2jmobius.gameserver.model.entity.Hero; import org.l2jmobius.gameserver.model.entity.Hero;
import org.l2jmobius.gameserver.model.events.EventDispatcher; import org.l2jmobius.gameserver.model.events.EventDispatcher;
@@ -396,10 +397,8 @@ public class GameServer
ItemsAutoDestroy.getInstance(); ItemsAutoDestroy.getInstance();
} }
if (Config.ALLOW_RACE)
{
MonsterRace.getInstance(); MonsterRace.getInstance();
}
TaskManager.getInstance(); TaskManager.getInstance();
AntiFeedManager.getInstance().registerEvent(AntiFeedManager.GAME_ID); AntiFeedManager.getInstance().registerEvent(AntiFeedManager.GAME_ID);

View File

@@ -1,140 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.data.xml.impl.NpcData;
import org.l2jmobius.gameserver.model.actor.Npc;
import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate;
public class MonsterRace
{
protected static final Logger LOGGER = Logger.getLogger(MonsterRace.class.getName());
private final Npc[] _monsters;
private int[][] _speeds;
private final int[] _first;
private final int[] _second;
protected MonsterRace()
{
_monsters = new Npc[8];
_speeds = new int[8][20];
_first = new int[2];
_second = new int[2];
}
public void newRace()
{
int random = 0;
for (int i = 0; i < 8; i++)
{
final int id = 31003;
random = Rnd.get(24);
while (true)
{
for (int j = i - 1; j >= 0; j--)
{
if (_monsters[j].getTemplate().getId() == (id + random))
{
random = Rnd.get(24);
}
}
break;
}
try
{
final NpcTemplate template = NpcData.getInstance().getTemplate(id + random);
_monsters[i] = (Npc) Class.forName("org.l2jmobius.gameserver.model.actor.instance." + template.getType() + "Instance").getConstructors()[0].newInstance(template);
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Unable to create monster!", e);
}
}
newSpeeds();
}
public void newSpeeds()
{
_speeds = new int[8][20];
int total = 0;
_first[1] = 0;
_second[1] = 0;
for (int i = 0; i < 8; i++)
{
total = 0;
for (int j = 0; j < 20; j++)
{
_speeds[i][j] = j == 19 ? 100 : Rnd.get(60) + 65;
total += _speeds[i][j];
}
if (total >= _first[1])
{
_second[0] = _first[0];
_second[1] = _first[1];
_first[0] = 8 - i;
_first[1] = total;
}
else if (total >= _second[1])
{
_second[0] = 8 - i;
_second[1] = total;
}
}
}
/**
* @return Returns the monsters.
*/
public Npc[] getMonsters()
{
return _monsters;
}
/**
* @return Returns the speeds.
*/
public int[][] getSpeeds()
{
return _speeds;
}
public int getFirstPlace()
{
return _first[0];
}
public int getSecondPlace()
{
return _second[0];
}
public static MonsterRace getInstance()
{
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder
{
protected static final MonsterRace INSTANCE = new MonsterRace();
}
}

View File

@@ -0,0 +1,623 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.instancemanager.games;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.l2jmobius.Config;
import org.l2jmobius.commons.concurrent.ThreadPool;
import org.l2jmobius.commons.database.DatabaseFactory;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.data.xml.impl.NpcData;
import org.l2jmobius.gameserver.model.actor.Npc;
import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate;
import org.l2jmobius.gameserver.model.zone.type.DerbyTrackZone;
import org.l2jmobius.gameserver.network.SystemMessageId;
import org.l2jmobius.gameserver.network.serverpackets.DeleteObject;
import org.l2jmobius.gameserver.network.serverpackets.MonRaceInfo;
import org.l2jmobius.gameserver.network.serverpackets.PlaySound;
import org.l2jmobius.gameserver.network.serverpackets.SystemMessage;
import org.l2jmobius.gameserver.util.Broadcast;
public class MonsterRace
{
protected static final Logger LOGGER = Logger.getLogger(MonsterRace.class.getName());
public static enum RaceState
{
ACCEPTING_BETS,
WAITING,
STARTING_RACE,
RACE_END
}
protected static final PlaySound SOUND_1 = new PlaySound(1, "S_Race", 0, 0, 0, 0, 0);
protected static final PlaySound SOUND_2 = new PlaySound("ItemSound2.race_start");
protected static final int[][] CODES =
{
{
-1,
0
},
{
0,
15322
},
{
13765,
-1
}
};
protected final List<Integer> _npcTemplates = new ArrayList<>(); // List holding npc templates, shuffled on a new race.
protected final List<HistoryInfo> _history = new ArrayList<>(); // List holding old race records.
protected final Map<Integer, Long> _betsPerLane = new ConcurrentHashMap<>(); // Map holding all bets for each lane ; values setted to 0 after every race.
protected final List<Double> _odds = new ArrayList<>(); // List holding sorted odds per lane ; cleared at new odds calculation.
protected int _raceNumber = 1;
protected int _finalCountdown = 0;
protected RaceState _state = RaceState.RACE_END;
protected MonRaceInfo _packet;
private final Npc[] _monsters = new Npc[8];
private int[][] _speeds = new int[8][20];
private final int[] _first = new int[2];
private final int[] _second = new int[2];
protected MonsterRace()
{
if (!Config.ALLOW_RACE)
{
return;
}
// Feed _history with previous race results.
loadHistory();
// Feed _betsPerLane with stored informations on bets.
loadBets();
// Feed _npcTemplates, we will only have to shuffle it when needed.
for (int i = 31003; i < 31027; i++)
{
_npcTemplates.add(i);
}
ThreadPool.scheduleAtFixedRate(new Announcement(), 0, 1000);
}
public static class HistoryInfo
{
private final int _raceId;
private int _first;
private int _second;
private double _oddRate;
public HistoryInfo(int raceId, int first, int second, double oddRate)
{
_raceId = raceId;
_first = first;
_second = second;
_oddRate = oddRate;
}
public int getRaceId()
{
return _raceId;
}
public int getFirst()
{
return _first;
}
public int getSecond()
{
return _second;
}
public double getOddRate()
{
return _oddRate;
}
public void setFirst(int first)
{
_first = first;
}
public void setSecond(int second)
{
_second = second;
}
public void setOddRate(double oddRate)
{
_oddRate = oddRate;
}
}
private class Announcement implements Runnable
{
public Announcement()
{
}
@Override
public void run()
{
if (_finalCountdown > 1200)
{
_finalCountdown = 0;
}
switch (_finalCountdown)
{
case 0:
{
newRace();
newSpeeds();
_state = RaceState.ACCEPTING_BETS;
_packet = new MonRaceInfo(CODES[0][0], CODES[0][1], getMonsters(), getSpeeds());
final SystemMessage msg = new SystemMessage(SystemMessageId.TICKETS_ARE_NOW_AVAILABLE_FOR_MONSTER_RACE_S1);
msg.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, _packet, msg);
break;
}
case 30: // 30 sec
case 60: // 1 min
case 90: // 1 min 30 sec
case 120: // 2 min
case 150: // 2 min 30
case 180: // 3 min
case 210: // 3 min 30
case 240: // 4 min
case 270: // 4 min 30 sec
case 330: // 5 min 30 sec
case 360: // 6 min
case 390: // 6 min 30 sec
case 420: // 7 min
case 450: // 7 min 30
case 480: // 8 min
case 510: // 8 min 30
case 540: // 9 min
case 570: // 9 min 30 sec
case 630: // 10 min 30 sec
case 660: // 11 min
case 690: // 11 min 30 sec
case 720: // 12 min
case 750: // 12 min 30
case 780: // 13 min
case 810: // 13 min 30
case 870: // 14 min 30 sec
{
final SystemMessage msg = new SystemMessage(SystemMessageId.TICKETS_ARE_NOW_AVAILABLE_FOR_MONSTER_RACE_S1);
msg.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg);
break;
}
case 300: // 5 min
{
final SystemMessage msg = new SystemMessage(SystemMessageId.NOW_SELLING_TICKETS_FOR_MONSTER_RACE_S1);
msg.addInt(_raceNumber);
final SystemMessage msg2 = new SystemMessage(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S);
msg2.addInt(10);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg, msg2);
break;
}
case 600: // 10 min
{
final SystemMessage msg = new SystemMessage(SystemMessageId.NOW_SELLING_TICKETS_FOR_MONSTER_RACE_S1);
msg.addInt(_raceNumber);
final SystemMessage msg2 = new SystemMessage(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S);
msg2.addInt(5);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg, msg2);
break;
}
case 840: // 14 min
{
final SystemMessage msg = new SystemMessage(SystemMessageId.NOW_SELLING_TICKETS_FOR_MONSTER_RACE_S1);
msg.addInt(_raceNumber);
final SystemMessage msg2 = new SystemMessage(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S);
msg2.addInt(1);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg, msg2);
break;
}
case 900: // 15 min
{
_state = RaceState.WAITING;
calculateOdds();
final SystemMessage msg = new SystemMessage(SystemMessageId.TICKETS_ARE_NOW_AVAILABLE_FOR_MONSTER_RACE_S1);
msg.addInt(_raceNumber);
final SystemMessage msg2 = new SystemMessage(SystemMessageId.TICKETS_SALES_ARE_CLOSED_FOR_MONSTER_RACE_S1_ODDS_ARE_POSTED);
msg2.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg, msg2);
break;
}
case 960: // 16 min
case 1020: // 17 min
{
final int minutes = (_finalCountdown == 960) ? 2 : 1;
final SystemMessage msg = new SystemMessage(SystemMessageId.MONSTER_RACE_S2_WILL_BEGIN_IN_S1_MINUTE_S);
msg.addInt(minutes);
msg.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg);
break;
}
case 1050: // 17 min 30 sec
{
final SystemMessage msg = new SystemMessage(SystemMessageId.MONSTER_RACE_S1_WILL_BEGIN_IN_30_SECONDS);
msg.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg);
break;
}
case 1070: // 17 min 50 sec
{
final SystemMessage msg = new SystemMessage(SystemMessageId.MONSTER_RACE_S1_IS_ABOUT_TO_BEGIN_COUNTDOWN_IN_FIVE_SECONDS);
msg.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg);
break;
}
case 1075: // 17 min 55 sec
case 1076: // 17 min 56 sec
case 1077: // 17 min 57 sec
case 1078: // 17 min 58 sec
case 1079: // 17 min 59 sec
{
final int seconds = 1080 - _finalCountdown;
final SystemMessage msg = new SystemMessage(SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SECOND_S);
msg.addInt(seconds);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg);
break;
}
case 1080: // 18 min
{
_state = RaceState.STARTING_RACE;
_packet = new MonRaceInfo(CODES[1][0], CODES[1][1], getMonsters(), getSpeeds());
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, new SystemMessage(SystemMessageId.THEY_RE_OFF), SOUND_1, SOUND_2, _packet);
break;
}
case 1085: // 18 min 5 sec
{
_packet = new MonRaceInfo(CODES[2][0], CODES[2][1], getMonsters(), getSpeeds());
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, _packet);
break;
}
case 1115: // 18 min 35 sec
{
_state = RaceState.RACE_END;
// Populate history info with data, stores it in database.
final HistoryInfo info = _history.get(_history.size() - 1);
info.setFirst(getFirstPlace());
info.setSecond(getSecondPlace());
info.setOddRate(_odds.get(getFirstPlace() - 1));
saveHistory(info);
clearBets();
final SystemMessage msg = new SystemMessage(SystemMessageId.FIRST_PRIZE_GOES_TO_THE_PLAYER_IN_LANE_S1_SECOND_PRIZE_GOES_TO_THE_PLAYER_IN_LANE_S2);
msg.addInt(getFirstPlace());
msg.addInt(getSecondPlace());
final SystemMessage msg2 = new SystemMessage(SystemMessageId.MONSTER_RACE_S1_IS_FINISHED);
msg2.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg, msg2);
_raceNumber++;
break;
}
case 1140: // 19 min
{
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, new DeleteObject(getMonsters()[0]), new DeleteObject(getMonsters()[1]), new DeleteObject(getMonsters()[2]), new DeleteObject(getMonsters()[3]), new DeleteObject(getMonsters()[4]), new DeleteObject(getMonsters()[5]), new DeleteObject(getMonsters()[6]), new DeleteObject(getMonsters()[7]));
break;
}
}
_finalCountdown += 1;
}
}
public void newRace()
{
// Edit _history.
_history.add(new HistoryInfo(_raceNumber, 0, 0, 0));
// Randomize _npcTemplates.
Collections.shuffle(_npcTemplates);
// Setup 8 new creatures ; pickup the first 8 from _npcTemplates.
for (int i = 0; i < 8; i++)
{
try
{
final NpcTemplate template = NpcData.getInstance().getTemplate(_npcTemplates.get(i));
_monsters[i] = (Npc) Class.forName("org.l2jmobius.gameserver.model.actor.instance." + template.getType() + "Instance").getConstructors()[0].newInstance(template);
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "", e);
}
}
}
public void newSpeeds()
{
_speeds = new int[8][20];
int total = 0;
_first[1] = 0;
_second[1] = 0;
for (int i = 0; i < 8; i++)
{
total = 0;
for (int j = 0; j < 20; j++)
{
if (j == 19)
{
_speeds[i][j] = 100;
}
else
{
_speeds[i][j] = Rnd.get(60) + 65;
}
total += _speeds[i][j];
}
if (total >= _first[1])
{
_second[0] = _first[0];
_second[1] = _first[1];
_first[0] = 8 - i;
_first[1] = total;
}
else if (total >= _second[1])
{
_second[0] = 8 - i;
_second[1] = total;
}
}
}
/**
* Load past races informations, feeding _history arrayList.<br>
* Also sets _raceNumber, based on latest HistoryInfo loaded.
*/
protected void loadHistory()
{
try (Connection con = DatabaseFactory.getConnection())
{
PreparedStatement statement = con.prepareStatement("SELECT * FROM mdt_history");
ResultSet rset = statement.executeQuery();
while (rset.next())
{
_history.add(new HistoryInfo(rset.getInt("race_id"), rset.getInt("first"), rset.getInt("second"), rset.getDouble("odd_rate")));
_raceNumber++;
}
rset.close();
statement.close();
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, "MonsterRace: Can't load history: " + e.getMessage(), e);
}
LOGGER.info("MonsterRace: loaded " + _history.size() + " records, currently on race #" + _raceNumber);
}
/**
* Save an history record into database.
* @param history The infos to store.
*/
protected void saveHistory(HistoryInfo history)
{
try (Connection con = DatabaseFactory.getConnection())
{
PreparedStatement statement = con.prepareStatement("INSERT INTO mdt_history (race_id, first, second, odd_rate) VALUES (?,?,?,?)");
statement.setInt(1, history.getRaceId());
statement.setInt(2, history.getFirst());
statement.setInt(3, history.getSecond());
statement.setDouble(4, history.getOddRate());
statement.execute();
statement.close();
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, "MonsterRace: Can't save history: " + e.getMessage(), e);
}
}
/**
* Load current bets per lane ; initialize the map keys.
*/
protected void loadBets()
{
try (Connection con = DatabaseFactory.getConnection())
{
PreparedStatement statement = con.prepareStatement("SELECT * FROM mdt_bets");
ResultSet rset = statement.executeQuery();
while (rset.next())
{
setBetOnLane(rset.getInt("lane_id"), rset.getLong("bet"), false);
}
rset.close();
statement.close();
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, "MonsterRace: Can't load bets: " + e.getMessage(), e);
}
}
/**
* Save the current lane bet into database.
* @param lane : The lane to affect.
* @param sum : The sum to set.
*/
protected void saveBet(int lane, long sum)
{
try (Connection con = DatabaseFactory.getConnection())
{
PreparedStatement statement = con.prepareStatement("REPLACE INTO mdt_bets (lane_id, bet) VALUES (?,?)");
statement.setInt(1, lane);
statement.setLong(2, sum);
statement.execute();
statement.close();
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, "MonsterRace: Can't save bet: " + e.getMessage(), e);
}
}
/**
* Clear all lanes bets, either on database or Map.
*/
protected void clearBets()
{
for (int key : _betsPerLane.keySet())
{
_betsPerLane.put(key, 0L);
}
try (Connection con = DatabaseFactory.getConnection())
{
PreparedStatement statement = con.prepareStatement("UPDATE mdt_bets SET bet = 0");
statement.execute();
statement.close();
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, "MonsterRace: Can't clear bets: " + e.getMessage(), e);
}
}
/**
* Setup lane bet, based on previous value (if any).
* @param lane : The lane to edit.
* @param amount : The amount to add.
* @param saveOnDb : Should it be saved on db or not.
*/
public void setBetOnLane(int lane, long amount, boolean saveOnDb)
{
final long sum = (_betsPerLane.containsKey(lane)) ? _betsPerLane.get(lane) + amount : amount;
_betsPerLane.put(lane, sum);
if (saveOnDb)
{
saveBet(lane, sum);
}
}
/**
* Calculate odds for every lane, based on others lanes.
*/
protected void calculateOdds()
{
// Clear previous List holding old odds.
_odds.clear();
// Sort bets lanes per lane.
final Map<Integer, Long> sortedLanes = new TreeMap<>(_betsPerLane);
// Pass a first loop in order to calculate total sum of all lanes.
long sumOfAllLanes = 0;
for (long amount : sortedLanes.values())
{
sumOfAllLanes += amount;
}
// As we get the sum, we can now calculate the odd rate of each lane.
for (long amount : sortedLanes.values())
{
_odds.add((amount == 0) ? 0D : Math.max(1.25, (sumOfAllLanes * 0.7) / amount));
}
}
public Npc[] getMonsters()
{
return _monsters;
}
public int[][] getSpeeds()
{
return _speeds;
}
public int getFirstPlace()
{
return _first[0];
}
public int getSecondPlace()
{
return _second[0];
}
public MonRaceInfo getRacePacket()
{
return _packet;
}
public RaceState getCurrentRaceState()
{
return _state;
}
public int getRaceNumber()
{
return _raceNumber;
}
public List<HistoryInfo> getHistory()
{
return _history;
}
public List<Double> getOdds()
{
return _odds;
}
public static MonsterRace getInstance()
{
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder
{
protected static final MonsterRace INSTANCE = new MonsterRace();
}
}

View File

@@ -16,69 +16,26 @@
*/ */
package org.l2jmobius.gameserver.model.actor.instance; package org.l2jmobius.gameserver.model.actor.instance;
import java.util.Collection; import java.util.List;
import java.util.concurrent.ConcurrentHashMap; import java.util.Locale;
import org.l2jmobius.Config; import org.l2jmobius.Config;
import org.l2jmobius.commons.concurrent.ThreadPool; import org.l2jmobius.commons.util.StringUtil;
import org.l2jmobius.gameserver.MonsterRace;
import org.l2jmobius.gameserver.enums.InstanceType;
import org.l2jmobius.gameserver.idfactory.IdFactory; import org.l2jmobius.gameserver.idfactory.IdFactory;
import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.instancemanager.games.MonsterRace;
import org.l2jmobius.gameserver.instancemanager.games.MonsterRace.HistoryInfo;
import org.l2jmobius.gameserver.instancemanager.games.MonsterRace.RaceState;
import org.l2jmobius.gameserver.model.actor.Npc; import org.l2jmobius.gameserver.model.actor.Npc;
import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate; import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate;
import org.l2jmobius.gameserver.model.itemcontainer.Inventory;
import org.l2jmobius.gameserver.model.items.instance.ItemInstance; import org.l2jmobius.gameserver.model.items.instance.ItemInstance;
import org.l2jmobius.gameserver.network.SystemMessageId; import org.l2jmobius.gameserver.network.SystemMessageId;
import org.l2jmobius.gameserver.network.serverpackets.ActionFailed; import org.l2jmobius.gameserver.network.serverpackets.ActionFailed;
import org.l2jmobius.gameserver.network.serverpackets.DeleteObject;
import org.l2jmobius.gameserver.network.serverpackets.IClientOutgoingPacket;
import org.l2jmobius.gameserver.network.serverpackets.InventoryUpdate;
import org.l2jmobius.gameserver.network.serverpackets.MonRaceInfo;
import org.l2jmobius.gameserver.network.serverpackets.NpcHtmlMessage; import org.l2jmobius.gameserver.network.serverpackets.NpcHtmlMessage;
import org.l2jmobius.gameserver.network.serverpackets.PlaySound;
import org.l2jmobius.gameserver.network.serverpackets.SystemMessage; import org.l2jmobius.gameserver.network.serverpackets.SystemMessage;
import org.l2jmobius.gameserver.util.Broadcast;
public class RaceManagerInstance extends Npc public class RaceManagerInstance extends Npc
{ {
public static final int LANES = 8; protected static final int TICKET_PRICES[] =
public static final int WINDOW_START = 0;
private static Collection<RaceManagerInstance> _managers;
protected static int _raceNumber = 4;
// Time Constants
private static final long SECOND = 1000;
private static final long MINUTE = 60 * SECOND;
private static int _minutes = 5;
// States
private static final int ACCEPTING_BETS = 0;
private static final int WAITING = 1;
private static final int STARTING_RACE = 2;
private static final int RACE_END = 3;
private static int _state = RACE_END;
protected static final int[][] _codes =
{
{
-1,
0
},
{
0,
15322
},
{
13765,
-1
}
};
private static boolean _notInitialized = true;
protected static MonRaceInfo _packet;
protected static final int[] _cost =
{ {
100, 100,
500, 500,
@@ -93,223 +50,167 @@ public class RaceManagerInstance extends Npc
public RaceManagerInstance(NpcTemplate template) public RaceManagerInstance(NpcTemplate template)
{ {
super(template); super(template);
setInstanceType(InstanceType.RaceManagerInstance);
if (!Config.ALLOW_RACE)
{
return;
}
if (_notInitialized)
{
_notInitialized = false;
_managers = ConcurrentHashMap.newKeySet();
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKETS_ARE_NOW_AVAILABLE_FOR_MONSTER_RACE_S1), 0, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.NOW_SELLING_TICKETS_FOR_MONSTER_RACE_S1), 30 * SECOND, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKETS_ARE_NOW_AVAILABLE_FOR_MONSTER_RACE_S1), MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.NOW_SELLING_TICKETS_FOR_MONSTER_RACE_S1), MINUTE + (30 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S), 2 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S), 3 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S), 4 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S), 5 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S), 6 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKETS_SALES_ARE_CLOSED_FOR_MONSTER_RACE_S1_ODDS_ARE_POSTED), 7 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.MONSTER_RACE_S2_WILL_BEGIN_IN_S1_MINUTE_S), 7 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.MONSTER_RACE_S2_WILL_BEGIN_IN_S1_MINUTE_S), 8 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.MONSTER_RACE_S1_WILL_BEGIN_IN_30_SECONDS), (8 * MINUTE) + (30 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.MONSTER_RACE_S1_IS_ABOUT_TO_BEGIN_COUNTDOWN_IN_FIVE_SECONDS), (8 * MINUTE) + (50 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SECOND_S), (8 * MINUTE) + (55 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SECOND_S), (8 * MINUTE) + (56 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SECOND_S), (8 * MINUTE) + (57 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SECOND_S), (8 * MINUTE) + (58 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SECOND_S), (8 * MINUTE) + (59 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.THEY_RE_OFF), 9 * MINUTE, 10 * MINUTE);
}
_managers.add(this);
}
@Override
public boolean isAutoAttackable(Creature attacker)
{
if (attacker.isMonster())
{
return true;
}
return super.isAutoAttackable(attacker);
}
class Announcement implements Runnable
{
private final SystemMessageId _type;
public Announcement(SystemMessageId pType)
{
_type = pType;
}
@Override
public void run()
{
makeAnnouncement(_type);
}
}
public void makeAnnouncement(SystemMessageId type)
{
final SystemMessage sm = new SystemMessage(type);
switch (type.getId())
{
case 816: // SystemMessageId.TICKETS_ARE_NOW_AVAILABLE_FOR_MONSTER_RACE_S1
case 817: // SystemMessageId.NOW_SELLING_TICKETS_FOR_MONSTER_RACE_S1
{
if (_state != ACCEPTING_BETS)
{// LOGGER.info("Race Initializing");
_state = ACCEPTING_BETS;
startRace();
} // else{LOGGER.info("Race open");}
sm.addInt(_raceNumber);
break;
}
case 818: // SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S
case 820: // SystemMessageId.MONSTER_RACE_S2_WILL_BEGIN_IN_S1_MINUTE_S
case 823: // SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SECOND_S
{
sm.addInt(_minutes);
if (type.getId() == 820)
{
sm.addInt(_raceNumber);
}
_minutes--;
break;
}
case 819: // SystemMessageId.TICKETS_SALES_ARE_CLOSED_FOR_MONSTER_RACE_S1_ODDS_ARE_POSTED
{
// LOGGER.info("Sales closed");
sm.addInt(_raceNumber);
_state = WAITING;
_minutes = 2;
break;
}
case 821: // SystemMessageId.MONSTER_RACE_S1_WILL_BEGIN_IN_30_SECONDS
case 822: // SystemMessageId.MONSTER_RACE_S1_IS_ABOUT_TO_BEGIN_COUNTDOWN_IN_FIVE_SECONDS
case 825: // SystemMessageId.MONSTER_RACE_S1_IS_FINISHED
{
sm.addInt(_raceNumber);
_minutes = 5;
break;
}
case 826: // SystemMessageId.FIRST_PRIZE_GOES_TO_THE_PLAYER_IN_LANE_S1_SECOND_PRIZE_GOES_TO_THE_PLAYER_IN_LANE_S2
{
// LOGGER.info("Placing");
_state = RACE_END;
sm.addInt(MonsterRace.getInstance().getFirstPlace());
sm.addInt(MonsterRace.getInstance().getSecondPlace());
break;
}
}
// _logn.info("Counter: "+minutes);
// LOGGER.info("State: "+state);
broadcast(sm);
// LOGGER.info("Player's known: "+getKnownPlayers().size());
if (type == SystemMessageId.THEY_RE_OFF)
{
// LOGGER.info("Starting race");
_state = STARTING_RACE;
startRace();
_minutes = 5;
}
}
protected void broadcast(IClientOutgoingPacket pkt)
{
for (RaceManagerInstance manager : _managers)
{
if (!manager.isDead())
{
Broadcast.toKnownPlayers(manager, pkt);
}
}
}
public void sendMonsterInfo()
{
broadcast(_packet);
}
private void startRace()
{
final MonsterRace race = MonsterRace.getInstance();
if (_state == STARTING_RACE)
{
// state++;
broadcast(new PlaySound(1, "S_Race", 0, 0, 0, 0, 0));
broadcast(new PlaySound(0, "ItemSound2.race_start", 1, 121209259, 12125, 182487, -3559));
_packet = new MonRaceInfo(_codes[1][0], _codes[1][1], race.getMonsters(), race.getSpeeds());
sendMonsterInfo();
ThreadPool.schedule(new RunRace(), 5000);
}
else
{
// state++;
race.newRace();
race.newSpeeds();
_packet = new MonRaceInfo(_codes[0][0], _codes[0][1], race.getMonsters(), race.getSpeeds());
sendMonsterInfo();
}
} }
@Override @Override
public void onBypassFeedback(PlayerInstance player, String command) public void onBypassFeedback(PlayerInstance player, String command)
{ {
if (command.startsWith("BuyTicket") && (_state != ACCEPTING_BETS))
{
if (!Config.ALLOW_RACE)
{
return;
}
player.sendPacket(SystemMessageId.MONSTER_RACE_TICKETS_ARE_NO_LONGER_AVAILABLE);
command = "Chat 0";
}
if (command.startsWith("ShowOdds") && (_state == ACCEPTING_BETS))
{
if (!Config.ALLOW_RACE)
{
return;
}
player.sendPacket(SystemMessageId.MONSTER_RACE_PAYOUT_INFORMATION_IS_NOT_AVAILABLE_WHILE_TICKETS_ARE_BEING_SOLD);
command = "Chat 0";
}
if (command.startsWith("BuyTicket")) if (command.startsWith("BuyTicket"))
{ {
if (!Config.ALLOW_RACE) if (!Config.ALLOW_RACE || (MonsterRace.getInstance().getCurrentRaceState() != RaceState.ACCEPTING_BETS))
{ {
player.sendPacket(SystemMessageId.MONSTER_RACE_TICKETS_ARE_NO_LONGER_AVAILABLE);
super.onBypassFeedback(player, "Chat 0");
return; return;
} }
int val = Integer.parseInt(command.substring(10)); int val = Integer.parseInt(command.substring(10));
if (val == 0) if (val == 0)
{ {
player.setRace(0, 0); player.setRace(0, 0);
player.setRace(1, 0); player.setRace(1, 0);
} }
if (((val == 10) && (player.getRace(0) == 0)) || ((val == 20) && (player.getRace(0) == 0) && (player.getRace(1) == 0))) if (((val == 10) && (player.getRace(0) == 0)) || ((val == 20) && (player.getRace(0) == 0) && (player.getRace(1) == 0)))
{ {
val = 0; val = 0;
} }
showBuyTicket(player, val);
} String search, replace;
else if (command.equals("ShowOdds"))
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
if (val < 10)
{ {
if (!Config.ALLOW_RACE) html.setFile(player, getHtmlPath(getId(), 2, player));
for (int i = 0; i < 8; i++)
{
int n = i + 1;
search = "Mob" + n;
html.replace(search, MonsterRace.getInstance().getMonsters()[i].getTemplate().getName());
}
search = "No1";
if (val == 0)
{
html.replace(search, "");
}
else
{
html.replace(search, val);
player.setRace(0, val);
}
}
else if (val < 20)
{
if (player.getRace(0) == 0)
{ {
return; return;
} }
showOdds(player);
html.setFile(player, getHtmlPath(getId(), 3, player));
html.replace("0place", player.getRace(0));
search = "Mob1";
replace = MonsterRace.getInstance().getMonsters()[player.getRace(0) - 1].getTemplate().getName();
html.replace(search, replace);
search = "0adena";
if (val == 10)
{
html.replace(search, "");
}
else
{
html.replace(search, TICKET_PRICES[val - 11]);
player.setRace(1, val - 10);
}
}
else if (val == 20)
{
if ((player.getRace(0) == 0) || (player.getRace(1) == 0))
{
return;
}
html.setFile(player, getHtmlPath(getId(), 4, player));
html.replace("0place", player.getRace(0));
search = "Mob1";
replace = MonsterRace.getInstance().getMonsters()[player.getRace(0) - 1].getTemplate().getName();
html.replace(search, replace);
search = "0adena";
int price = TICKET_PRICES[player.getRace(1) - 1];
html.replace(search, price);
search = "0tax";
int tax = 0;
html.replace(search, tax);
search = "0total";
int total = price + tax;
html.replace(search, total);
}
else
{
if ((player.getRace(0) == 0) || (player.getRace(1) == 0))
{
return;
}
int ticket = player.getRace(0);
int priceId = player.getRace(1);
if (!player.reduceAdena("Race", TICKET_PRICES[priceId - 1], this, true))
{
return;
}
player.setRace(0, 0);
player.setRace(1, 0);
ItemInstance item = new ItemInstance(IdFactory.getInstance().getNextId(), 4443);
item.setCount(1);
item.setEnchantLevel(MonsterRace.getInstance().getRaceNumber());
item.setCustomType1(ticket);
item.setCustomType2(TICKET_PRICES[priceId - 1] / 100);
player.addItem("Race", item, player, false);
final SystemMessage msg = new SystemMessage(SystemMessageId.ACQUIRED_S1_S2);
msg.addInt(MonsterRace.getInstance().getRaceNumber());
msg.addItemName(4443);
player.sendPacket(msg);
// Refresh lane bet.
MonsterRace.getInstance().setBetOnLane(ticket, TICKET_PRICES[priceId - 1], true);
super.onBypassFeedback(player, "Chat 0");
return;
}
html.replace("1race", MonsterRace.getInstance().getRaceNumber());
html.replace("%objectId%", getObjectId());
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
else if (command.equals("ShowOdds"))
{
if (!Config.ALLOW_RACE || (MonsterRace.getInstance().getCurrentRaceState() == RaceState.ACCEPTING_BETS))
{
player.sendPacket(SystemMessageId.MONSTER_RACE_PAYOUT_INFORMATION_IS_NOT_AVAILABLE_WHILE_TICKETS_ARE_BEING_SOLD);
super.onBypassFeedback(player, "Chat 0");
return;
}
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
html.setFile(player, getHtmlPath(getId(), 5, player));
for (int i = 0; i < 8; i++)
{
final int n = i + 1;
html.replace("Mob" + n, MonsterRace.getInstance().getMonsters()[i].getTemplate().getName());
// Odd
final double odd = MonsterRace.getInstance().getOdds().get(i);
html.replace("Odd" + n, (odd > 0D) ? String.format(Locale.ENGLISH, "%.1f", odd) : "&$804;");
}
html.replace("1race", MonsterRace.getInstance().getRaceNumber());
html.replace("%objectId%", getObjectId());
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
} }
else if (command.equals("ShowInfo")) else if (command.equals("ShowInfo"))
{ {
@@ -317,269 +218,160 @@ public class RaceManagerInstance extends Npc
{ {
return; return;
} }
showMonsterInfo(player);
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
html.setFile(player, getHtmlPath(getId(), 6, player));
for (int i = 0; i < 8; i++)
{
int n = i + 1;
String search = "Mob" + n;
html.replace(search, MonsterRace.getInstance().getMonsters()[i].getTemplate().getName());
} }
else if (command.equals("calculateWin")) html.replace("%objectId%", getObjectId());
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
else if (command.equals("ShowTickets"))
{ {
if (!Config.ALLOW_RACE) if (!Config.ALLOW_RACE)
{ {
super.onBypassFeedback(player, "Chat 0");
return; return;
} }
// displayCalculateWinnings(player);
// Generate data.
final StringBuilder sb = new StringBuilder();
// Retrieve player's tickets.
for (ItemInstance ticket : player.getInventory().getAllItemsByItemId(4443))
{
// Don't list current race tickets.
if (ticket.getEnchantLevel() == MonsterRace.getInstance().getRaceNumber())
{
continue;
} }
else if (command.equals("viewHistory"))
StringUtil.append(sb, "<tr><td><a action=\"bypass -h npc_%objectId%_ShowTicket ", "" + ticket.getObjectId(), "\">", "" + ticket.getEnchantLevel(), " Race Number</a></td><td align=right><font color=\"LEVEL\">", "" + ticket.getCustomType1(), "</font> Number</td><td align=right><font color=\"LEVEL\">", "" + (ticket.getCustomType2() * 100), "</font> Adena</td></tr>");
}
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
html.setFile(player, getHtmlPath(getId(), 7, player));
html.replace("%tickets%", sb.toString());
html.replace("%objectId%", getObjectId());
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
else if (command.startsWith("ShowTicket"))
{
// Retrieve ticket objectId.
final int val = Integer.parseInt(command.substring(11));
if (!Config.ALLOW_RACE || (val == 0))
{
super.onBypassFeedback(player, "Chat 0");
return;
}
// Retrieve ticket on player's inventory.
final ItemInstance ticket = player.getInventory().getItemByObjectId(val);
if (ticket == null)
{
super.onBypassFeedback(player, "Chat 0");
return;
}
final int raceId = ticket.getEnchantLevel();
final int lane = ticket.getCustomType1();
final int bet = ticket.getCustomType2() * 100;
// Retrieve HistoryInfo for that race.
final HistoryInfo info = MonsterRace.getInstance().getHistory().get(raceId - 1);
if (info == null)
{
super.onBypassFeedback(player, "Chat 0");
return;
}
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
html.setFile(player, getHtmlPath(getId(), 8, player));
html.replace("%raceId%", raceId);
html.replace("%lane%", lane);
html.replace("%bet%", bet);
html.replace("%firstLane%", info.getFirst());
html.replace("%odd%", (lane == info.getFirst()) ? String.format(Locale.ENGLISH, "%.2f", info.getOddRate()) : "0.01");
html.replace("%objectId%", getObjectId());
html.replace("%ticketObjectId%", val);
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
else if (command.startsWith("CalculateWin"))
{
// Retrieve ticket objectId.
final int val = Integer.parseInt(command.substring(13));
if (!Config.ALLOW_RACE || (val == 0))
{
super.onBypassFeedback(player, "Chat 0");
return;
}
// Delete ticket on player's inventory.
final ItemInstance ticket = player.getInventory().getItemByObjectId(val);
if (ticket == null)
{
super.onBypassFeedback(player, "Chat 0");
return;
}
final int raceId = ticket.getEnchantLevel();
final int lane = ticket.getCustomType1();
final int bet = ticket.getCustomType2() * 100;
// Retrieve HistoryInfo for that race.
final HistoryInfo info = MonsterRace.getInstance().getHistory().get(raceId - 1);
if (info == null)
{
super.onBypassFeedback(player, "Chat 0");
return;
}
// Destroy the ticket.
if (player.destroyItem("MonsterTrack", ticket, this, true))
{
player.addAdena("MonsterTrack", (int) (bet * ((lane == info.getFirst()) ? info.getOddRate() : 0.01)), this, true);
}
super.onBypassFeedback(player, "Chat 0");
return;
}
else if (command.equals("ViewHistory"))
{ {
if (!Config.ALLOW_RACE) if (!Config.ALLOW_RACE)
{ {
super.onBypassFeedback(player, "Chat 0");
return; return;
} }
// displayHistory(player);
// Generate data.
final StringBuilder sb = new StringBuilder();
// Use whole history, pickup from 'last element' and stop at 'latest element - 7'.
final List<HistoryInfo> history = MonsterRace.getInstance().getHistory();
for (int i = history.size() - 1; i >= Math.max(0, history.size() - 7); i--)
{
final HistoryInfo info = history.get(i);
StringUtil.append(sb, "<tr><td><font color=\"LEVEL\">", "" + info.getRaceId(), "</font> th</td><td><font color=\"LEVEL\">", "" + info.getFirst(), "</font> Lane </td><td><font color=\"LEVEL\">", "" + info.getSecond(), "</font> Lane</td><td align=right><font color=00ffff>", String.format(Locale.ENGLISH, "%.2f", info.getOddRate()), "</font> Times</td></tr>");
}
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
html.setFile(player, getHtmlPath(getId(), 9, player));
html.replace("%infos%", sb.toString());
html.replace("%objectId%", getObjectId());
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
} }
else else
{ {
// getKnownList().removeKnownObject(player);
super.onBypassFeedback(player, command); super.onBypassFeedback(player, command);
} }
} }
public void showOdds(PlayerInstance player)
{
if (_state == ACCEPTING_BETS)
{
return;
}
final int npcId = getTemplate().getId();
String filename;
String search;
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
filename = getHtmlPath(npcId, 5, player);
html.setFile(player, filename);
for (int i = 0; i < 8; i++)
{
final int n = i + 1;
search = "Mob" + n;
html.replace(search, MonsterRace.getInstance().getMonsters()[i].getTemplate().getName());
}
html.replace("1race", String.valueOf(_raceNumber));
html.replace("%objectId%", String.valueOf(getObjectId()));
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
public void showMonsterInfo(PlayerInstance player)
{
final int npcId = getTemplate().getId();
String filename;
String search;
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
filename = getHtmlPath(npcId, 6, player);
html.setFile(player, filename);
for (int i = 0; i < 8; i++)
{
final int n = i + 1;
search = "Mob" + n;
html.replace(search, MonsterRace.getInstance().getMonsters()[i].getTemplate().getName());
}
html.replace("%objectId%", String.valueOf(getObjectId()));
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
public void showBuyTicket(PlayerInstance player, int value)
{
if (_state != ACCEPTING_BETS)
{
return;
}
final int npcId = getTemplate().getId();
SystemMessage sm;
String filename;
String search;
String replace;
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
if (value < 10)
{
filename = getHtmlPath(npcId, 2, player);
html.setFile(player, filename);
for (int i = 0; i < 8; i++)
{
final int n = i + 1;
search = "Mob" + n;
html.replace(search, MonsterRace.getInstance().getMonsters()[i].getTemplate().getName());
}
search = "No1";
if (value == 0)
{
html.replace(search, "");
}
else
{
html.replace(search, Integer.toString(value));
player.setRace(0, value);
}
}
else if (value < 20)
{
if (player.getRace(0) == 0)
{
return;
}
filename = getHtmlPath(npcId, 3, player);
html.setFile(player, filename);
html.replace("0place", Integer.toString(player.getRace(0)));
search = "Mob1";
replace = MonsterRace.getInstance().getMonsters()[player.getRace(0) - 1].getTemplate().getName();
html.replace(search, replace);
search = "0adena";
if (value == 10)
{
html.replace(search, "");
}
else
{
html.replace(search, Integer.toString(_cost[value - 11]));
player.setRace(1, value - 10);
}
}
else if (value == 20)
{
if ((player.getRace(0) == 0) || (player.getRace(1) == 0))
{
return;
}
filename = getHtmlPath(npcId, 4, player);
html.setFile(player, filename);
html.replace("0place", Integer.toString(player.getRace(0)));
search = "Mob1";
replace = MonsterRace.getInstance().getMonsters()[player.getRace(0) - 1].getTemplate().getName();
html.replace(search, replace);
search = "0adena";
final int price = _cost[player.getRace(1) - 1];
html.replace(search, Integer.toString(price));
search = "0tax";
final int tax = 0;
html.replace(search, Integer.toString(tax));
search = "0total";
final int total = price + tax;
html.replace(search, Integer.toString(total));
}
else
{
if ((player.getRace(0) == 0) || (player.getRace(1) == 0))
{
return;
}
final int ticket = player.getRace(0);
final int priceId = player.getRace(1);
if (!player.reduceAdena("Race", _cost[priceId - 1], this, true))
{
return;
}
player.setRace(0, 0);
player.setRace(1, 0);
sm = new SystemMessage(SystemMessageId.ACQUIRED_S1_S2);
sm.addInt(_raceNumber);
sm.addItemName(4443);
player.sendPacket(sm);
final ItemInstance item = new ItemInstance(IdFactory.getInstance().getNextId(), 4443);
item.setCount(1);
item.setEnchantLevel(_raceNumber);
item.setCustomType1(ticket);
item.setCustomType2(_cost[priceId - 1] / 100);
player.getInventory().addItem("Race", item, player, this);
final InventoryUpdate iu = new InventoryUpdate();
iu.addItem(item);
final ItemInstance adenaupdate = player.getInventory().getItemByItemId(Inventory.ADENA_ID);
iu.addModifiedItem(adenaupdate);
player.sendInventoryUpdate(iu);
return;
}
html.replace("1race", String.valueOf(_raceNumber));
html.replace("%objectId%", String.valueOf(getObjectId()));
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
public static class Race
{
private final Info[] _info;
public Race(Info[] pInfo)
{
_info = pInfo;
}
public Info getLaneInfo(int lane)
{
return _info[lane];
}
public class Info
{
private final int _id;
private final int _place;
private final int _odds;
private final int _payout;
public Info(int pId, int pPlace, int pOdds, int pPayout)
{
_id = pId;
_place = pPlace;
_odds = pOdds;
_payout = pPayout;
}
public int getId()
{
return _id;
}
public int getOdds()
{
return _odds;
}
public int getPayout()
{
return _payout;
}
public int getPlace()
{
return _place;
}
}
}
class RunRace implements Runnable
{
@Override
public void run()
{
_packet = new MonRaceInfo(_codes[2][0], _codes[2][1], MonsterRace.getInstance().getMonsters(), MonsterRace.getInstance().getSpeeds());
sendMonsterInfo();
ThreadPool.schedule(new RunEnd(), 30000);
}
}
class RunEnd implements Runnable
{
@Override
public void run()
{
makeAnnouncement(SystemMessageId.FIRST_PRIZE_GOES_TO_THE_PLAYER_IN_LANE_S1_SECOND_PRIZE_GOES_TO_THE_PLAYER_IN_LANE_S2);
makeAnnouncement(SystemMessageId.MONSTER_RACE_S1_IS_FINISHED);
_raceNumber++;
DeleteObject obj = null;
for (int i = 0; i < 8; i++)
{
obj = new DeleteObject(MonsterRace.getInstance().getMonsters()[i]);
broadcast(obj);
MonsterRace.getInstance().getMonsters()[i].deleteMe();
}
}
}
} }

View File

@@ -20,10 +20,12 @@ import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.l2jmobius.gameserver.enums.ChatType; import org.l2jmobius.gameserver.enums.ChatType;
import org.l2jmobius.gameserver.instancemanager.ZoneManager;
import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.World;
import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.actor.Creature;
import org.l2jmobius.gameserver.model.actor.Summon; import org.l2jmobius.gameserver.model.actor.Summon;
import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
import org.l2jmobius.gameserver.model.zone.ZoneType;
import org.l2jmobius.gameserver.network.serverpackets.CharInfo; import org.l2jmobius.gameserver.network.serverpackets.CharInfo;
import org.l2jmobius.gameserver.network.serverpackets.CreatureSay; import org.l2jmobius.gameserver.network.serverpackets.CreatureSay;
import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage; import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
@@ -191,4 +193,29 @@ public class Broadcast
{ {
toAllOnlinePlayers(new ExShowScreenMessage(text, 10000)); toAllOnlinePlayers(new ExShowScreenMessage(text, 10000));
} }
/**
* Send a packet to all players in a specific zone type.
* @param <T> ZoneType.
* @param zoneType : The zone type to send packets.
* @param packets : The packets to send.
*/
public static <T extends ZoneType> void toAllPlayersInZoneType(Class<T> zoneType, IClientOutgoingPacket... packets)
{
for (ZoneType zone : ZoneManager.getInstance().getAllZones(zoneType))
{
for (Creature creature : zone.getCharactersInside())
{
if (creature == null)
{
continue;
}
for (IClientOutgoingPacket packet : packets)
{
creature.sendPacket(packet);
}
}
}
}
} }

View File

@@ -0,0 +1,15 @@
CREATE TABLE IF NOT EXISTS `mdt_bets` (
`lane_id` INT(1) DEFAULT 0,
`bet` INT DEFAULT 0,
PRIMARY KEY (`lane_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;
INSERT INTO `mdt_bets` VALUES
('1','0'),
('2','0'),
('3','0'),
('4','0'),
('5','0'),
('6','0'),
('7','0'),
('8','0');

View File

@@ -0,0 +1,7 @@
CREATE TABLE IF NOT EXISTS `mdt_history` (
`race_id` MEDIUMINT DEFAULT 0,
`first` INT(1) DEFAULT 0,
`second` INT(1) DEFAULT 0,
`odd_rate` DOUBLE(10,2) DEFAULT 0,
PRIMARY KEY (`race_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;

View File

@@ -416,9 +416,6 @@
<admin command="admin_mobgroup_attackgrp" accessLevel="100" /> <admin command="admin_mobgroup_attackgrp" accessLevel="100" />
<admin command="admin_mobgroup_invul" accessLevel="100" /> <admin command="admin_mobgroup_invul" accessLevel="100" />
<!-- ADMIN MONSTER RACE -->
<admin command="admin_mons" accessLevel="100" />
<!-- ADMIN ONLINE --> <!-- ADMIN ONLINE -->
<admin command="admin_online" accessLevel="100" /> <admin command="admin_online" accessLevel="100" />

View File

@@ -1,19 +1,22 @@
<html><body><br><br> <html><body>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 0">Back</Button><br> <br>
In a monster race eight monsters race at the same time. If you guess which monster is expected to finish first and buy that monster's race ticket, based upon the odds to win you will receive an appropriate amount of Adena.<br> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 0">Go Back</Button>
<font color="LEVEL">How the Race Works</font>Monster races run every 20 minutes in real time.<br> <br>
<font color="LEVEL">How to Participate</font><br> In a monster race eight monsters race at the same time. If you guess which monster is expected to finish first and buy that monster's race ticket, based upon the odds to win you will receive an appropriate amount of adena.<br>
You can purchase a monster race ticket through a race manager NPC. Only between 18 minutes before the start of a race until 3 minutes before the race begins can you buy a ticket for the applicable race. When you purchase a ticket through a race manager, you can find out the names of the participating monsters and their current statistics. Three minutes before a race starts, the race manager will stop ticket sales and announce the monsters' individual odds for the current race.<br> <font color="LEVEL">Racing Schedule</font><br>
<font color="LEVEL">Betting</font><br> Monster races every 20 minutes in real time.<br>
There are two types of race betting. The Win bet means that you back a particular monster to win 1st place. The Place bet means that you will have to guess the 1st and 2nd place monsters, regardless of who finishes first or second.<br> <font color="LEVEL">Method of Race Participation</font><br>
You can purchase a monster race ticket through a race manager NPC. Only between 18 minutes before the start of a race until 3 minutes before the race begins you can buy a ticket for the applicable race. When you purchase a ticket through a conversation with a race manager, you can find out the names of participating monsters in the race and their current statistics. Five minutes before a race starts, the race manager will stop ticket sales and announce monsters' individual odds rate for the current race.<br>
<font color="LEVEL">Types of Race</font><br>
There are two types of race betting. The Win type betting means that you back a monster that will win the first prize. Place type of betting means that you will have to guess the 1st and 2nd winning monsters, regardless of who finishes first or second.<br>
<font color="00FFFF">Win</font><br1> <font color="00FFFF">Win</font><br1>
* Among the entire betting amount for Win betting, after subtracting the race track's own share of profit, the amount left will be your total winnings. Three minutes before the race, each monster's expected odds are announced. Each Win ticket's dividend amount is calculated by multiplying the odds by the price of a ticket. The odds are never less than 1.<br1> * Among the entire betting amount for Win betting, after subtracting the race track's own share of profit, the amount left will be your total winnings. Three minutes before the race each monster's expected odds are announced. Each Win ticket's dividend amount is calculated by multiplying the odds by the price of a ticket. The odds rate is never less than 1.<br1>
* The prize money does not get transferred to the next race.<br> * The prize money does not get transferred to the next race.<br>
<font color="00FFFF">Place</font><br1> <font color="00FFFF">Place</font><br1>
* Place betting is a way of betting when you correctly guess the two monsters who will finish first and second regardless of their order. The method of calculating the dividend amount is the same as Win betting.<br1> * Place betting is a way of betting when you correctly guess the two monsters who will finish first and second regardless of their order. The method of calculating dividend amount is the same as Win betting.<br>
* Currently place betting is not available.<br> * Currently place betting is not offered.<br>
<font color="LEVEL">End of Race</font><br> <font color="LEVEL">End of Race</font><br>
When a race is over, the race manager will shout the results of the race. You can trade your ticket for Adena by speaking to a race manager NPC. Check the Winning Information Data to find out the types of bets and total winnings for the race you bet on, as well as your win status and prize amount. If you won, you can receive your prize money thorough a race manager NPC. If you did not win, a race manager will buy back your ticket for a small amount of money.<br> When a race is over, the race manager will shout out the results of the race. Through a conversation with a race manager NPC, you can trade your ticket for adena. By looking up on the Winning Information Data, you can find out the types of tickets and quantity of the winnings in the race you betted on and your win status and prize amount you won. If you won, you can receive your prize money thorough a race manager NPC. If you did not win, a race manager will buy back your ticket by paying you a very small amount of money.<br>
If a server goes down while tickets are being sold, or while a race is in progress, the race will be cancelled. You can receive a full refund for your ticket for a cancelled race through a race manager.<br> If a server goes down while tickets are being sold, or while a race is in progress, the race will be cancelled. You can receive a full refund for your ticket for a cancelled race through a race manager.<br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 0">Back</Button> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 0">Go Back</Button>
</body></html> </body></html>

View File

@@ -3,6 +3,7 @@
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=1 height=3> <img src="L2UI.SquareBlank" width=1 height=3>
</center> </center>
<table width=270 border=0 bgcolor="111111"> <table width=270 border=0 bgcolor="111111">
<tr> <tr>
<td fixwidth=60>Lane Selection</td> <td fixwidth=60>Lane Selection</td>
@@ -11,6 +12,7 @@
<td fixwidth=30 align=right>Odds</td> <td fixwidth=30 align=right>Odds</td>
</tr> </tr>
</table> </table>
<table width=270 border=0> <table width=270 border=0>
<tr> <tr>
<td fixwidth=13><img src="L2UI.rightBtn2" width=14 height=13></td> <td fixwidth=13><img src="L2UI.rightBtn2" width=14 height=13></td>
@@ -20,18 +22,21 @@
<td fixwidth=30 align=right><font color="00ffff">13%</font></td> <td fixwidth=30 align=right><font color="00ffff">13%</font></td>
</tr> </tr>
</table> </table>
<table width=270 border=0> <table width=270 border=0>
<tr><td width=100>Select Bet Amount</td> <tr><td width=100>Select Bet Amount</td>
<td width=170> <td width=170>
<a action="bypass -h npc_%objectId%_BuyTicket 11" >100 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 12" >500 Adena</a><br1> <a action="bypass -h npc_%objectId%_BuyTicket 11" >100 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 12" >500 Adena</a> <br1>
<a action="bypass -h npc_%objectId%_BuyTicket 13" >1,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 14" >5,000 Adena</a><br1> <a action="bypass -h npc_%objectId%_BuyTicket 13" >1,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 14" >5,000 Adena</a> <br1>
<a action="bypass -h npc_%objectId%_BuyTicket 15" >10,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 16" >20,000 Adena</a><br1> <a action="bypass -h npc_%objectId%_BuyTicket 15" >10,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 16" >20,000 Adena</a> <br1>
<a action="bypass -h npc_%objectId%_BuyTicket 17" >50,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 18" >100,000 Adena</a><br1> <a action="bypass -h npc_%objectId%_BuyTicket 17" >50,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 18" >100,000 Adena</a> <br1>
</td></tr> </td></tr>
</table> </table>
<img src="L2UI.SquareBlank" width=270 height=10> <img src="L2UI.SquareBlank" width=270 height=10>
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=270 height=5> <img src="L2UI.SquareBlank" width=270 height=5>
<table width=270 border=0 cellpadding=0 cellspacing=0> <table width=270 border=0 cellpadding=0 cellspacing=0>
<tr> <tr>
<td width=70 height=20><button value="Select Lane" action="bypass -h npc_%objectId%_BuyTicket 0" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td> <td width=70 height=20><button value="Select Lane" action="bypass -h npc_%objectId%_BuyTicket 0" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td>
@@ -39,4 +44,5 @@
<td width=70 align=right><button value="Confirm" action="bypass -h npc_%objectId%_BuyTicket 20" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td> <td width=70 align=right><button value="Confirm" action="bypass -h npc_%objectId%_BuyTicket 20" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td>
</tr> </tr>
</table> </table>
</body></html> </body></html>

View File

@@ -11,6 +11,7 @@
<td fixwidth=30 align=right>Odds</td> <td fixwidth=30 align=right>Odds</td>
</tr> </tr>
</table> </table>
<table width=270 border=0> <table width=270 border=0>
<tr> <tr>
<td fixwidth=13><img src="L2UI.rightBtn2" width=14 height=13></td> <td fixwidth=13><img src="L2UI.rightBtn2" width=14 height=13></td>
@@ -20,6 +21,7 @@
<td fixwidth=30 align=right><font color="00ffff">13%</font></td> <td fixwidth=30 align=right><font color="00ffff">13%</font></td>
</tr> </tr>
</table> </table>
<table width=270 border=0 bgcolor=111111> <table width=270 border=0 bgcolor=111111>
<tr> <tr>
<td fixwidth=60>Bet Amount </td> <td fixwidth=60>Bet Amount </td>
@@ -37,11 +39,13 @@
<td>Total </td> <td>Total </td>
<td><font color="LEVEL">0total</font> Adena</td> <td><font color="LEVEL">0total</font> Adena</td>
</tr> </tr>
</table><br> </table>
<br>
<center>If the information is correct, please click Confirm.</center><br> <center>If the information is correct, please click Confirm.</center><br>
<img src="L2UI.SquareBlank" width=270 height=10> <img src="L2UI.SquareBlank" width=270 height=10>
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=270 height=5> <img src="L2UI.SquareBlank" width=270 height=5>
<table width=270 border=0 cellpadding=0 cellspacing=0> <table width=270 border=0 cellpadding=0 cellspacing=0>
<tr> <tr>
<td width=70 height=20><button value=&$141; action="bypass -h npc_%objectId%_Chat 0" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td> <td width=70 height=20><button value=&$141; action="bypass -h npc_%objectId%_Chat 0" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td>

View File

@@ -15,49 +15,49 @@
<tr> <tr>
<td fixwidth=40>No.1</td> <td fixwidth=40>No.1</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob1</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob1</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd1</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.2</td> <td fixwidth=40>No.2</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob2</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob2</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd2</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.3</td> <td fixwidth=40>No.3</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob3</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob3</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd3</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.4</td> <td fixwidth=40>No.4</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob4</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob4</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd4</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.5</td> <td fixwidth=40>No.5</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob5</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob5</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd5</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.6</td> <td fixwidth=40>No.6</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob6</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob6</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd6</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.7</td> <td fixwidth=40>No.7</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob7</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob7</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd7</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.8</td> <td fixwidth=40>No.8</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob8</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob8</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd8</font> </td>
</tr> </tr>
</table> </table>
<img src="L2UI.SquareBlank" width=270 height=10> <img src="L2UI.SquareBlank" width=270 height=10>

View File

@@ -3,22 +3,12 @@
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=1 height=3> <img src="L2UI.SquareBlank" width=1 height=3>
<table width=270 border=0 bgcolor=111111> <table width=270 border=0 bgcolor=111111>
<tr><td fixwidth=60>Winnings for a Race.</td><td fixwidth=60 align=right>Lane</td><td fixwidth=160 align=right>Bet Amount</td></tr> <tr><td fixwidth=70>Winnings for a Race.</td><td fixwidth=70 align=right>Lane</td><td fixwidth=130 align=right>Bet Amount</td></tr>
<tr><td fixwidth=60 fixheight=1></td><td fixwidth=60></td><td fixwidth=160></td></tr> <tr><td fixwidth=70 fixheight=1></td><td fixwidth=70></td><td fixwidth=130></td></tr>
</table> </table>
<table width=270 border=0> <table width=270 border=0>
<tr> %tickets%
<td><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 8">1 Race Number</Button></td>
<td align=right><font color="LEVEL">1</font> Number</td>
<td align=right><font color="LEVEL">100</font> Adena</td>
</tr>
</table> </table>
<img src="L2UI.SquareWhite" width=270 height=1>
<table width=270 border=0>
<tr><td width=90><Button ALIGN=LEFT ICON="NORMAL" action="bypass _mrsl01_1">Previous List</Button></td><td width=90 align=center>1 Page </td><td width=90 align=right><Button ALIGN=LEFT ICON="NORMAL" action="bypass _mrsl01_2"> Next List</Button></td></tr>
</table>
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=270 height=5> <img src="L2UI.SquareBlank" width=270 height=5>
<table width=270 border=0 cellpadding=0 cellspacing=0> <table width=270 border=0 cellpadding=0 cellspacing=0>

View File

@@ -4,11 +4,11 @@
<img src="L2UI.SquareBlank" width=1 height=3> <img src="L2UI.SquareBlank" width=1 height=3>
<table width=270 border=0> <table width=270 border=0>
<tr> <tr>
<td width=230>Race #19090<br> <td width=230>Race #%raceId%<br>
Lane 1<br> Lane: %lane%<br>
Bet: 100 adena<br> Bet: %bet% Adena<br>
1st place: Lane [4]<br> 1st place: Lane [%firstLane%]<br>
Odds:[0.01] Odds: [%odd%]
</td> </td>
</tr> </tr>
</table> </table>
@@ -18,8 +18,8 @@ Odds:[0.01]
<table width=270 border=0 cellpadding=0 cellspacing=0> <table width=270 border=0 cellpadding=0 cellspacing=0>
<tr> <tr>
<td width=90 height=20><button value="Previous" action="bypass -h npc_%objectId%_Chat 7" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td> <td width=90 height=20><button value="Previous" action="bypass -h npc_%objectId%_ShowTickets" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td>
<td width=90></td> <td width=90></td>
<td width=90 align=right><button value="Calculate" action="bypass _mrsl03_30635184" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td> <td width=90 align=right><button value="Calculate" action="bypass -h npc_%objectId%_CalculateWin %ticketObjectId%" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td>
</tr></table> </tr></table>
</body></html> </body></html>

View File

@@ -7,41 +7,7 @@
</table> </table>
<table width=270 border=0> <table width=270 border=0>
<tr><td fixwidth=60 fixheight=1></td><td fixwidth=70></td><td fixwidth=70></td><td fixwidth=70></td></tr> <tr><td fixwidth=60 fixheight=1></td><td fixwidth=70></td><td fixwidth=70></td><td fixwidth=70></td></tr>
<tr> %infos%
<td><font color="LEVEL">7</font> th</td>
<td><font color="LEVEL">8</font> Lane </td><td><font color="LEVEL">1</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">6</font> th</td>
<td><font color="LEVEL">4</font> Lane </td><td><font color="LEVEL">8</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">5</font> th</td>
<td><font color="LEVEL">7</font> Lane </td><td><font color="LEVEL">2</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">4</font> th</td>
<td><font color="LEVEL">6</font> Lane </td><td><font color="LEVEL">8</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">3</font> th</td>
<td><font color="LEVEL">4</font> Lane </td><td><font color="LEVEL">1</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">2</font> th</td>
<td><font color="LEVEL">1</font> Lane </td><td><font color="LEVEL">8</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">1</font> th</td>
<td><font color="LEVEL">7</font> Lane </td><td><font color="LEVEL">6</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
</table> </table>
<img src="L2UI.SquareBlank" width=270 height=10> <img src="L2UI.SquareBlank" width=270 height=10>
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>

View File

@@ -1,15 +1,15 @@
<html>
<body>
<center>Monster Race Manager</center> <center>Monster Race Manager</center>
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareBlank" width=1 height=3> <img src="L2UI.SquareWhite" width=270 height=1>
<table width=270 border=0> <img src="L2UI.SquareBlank" width=1 height=3>
<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 1">Monster Race Help</Button></td></tr> <br>
<!--<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_ShowOdds">View Odds</Button></td></tr>--> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 1">Monster Race Help</Button>
<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_ShowInfo">View Monster Information</Button></td></tr> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_ShowOdds">View Odds</Button>
<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_BuyTicket 0">Purchase Ticket</Button></td></tr> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_ShowInfo">View Monster Information</Button>
<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 7">Calculate Winnings</Button></td></tr> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_BuyTicket 0">Purchase Ticket</Button>
<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 9">View Past Results</Button></td></tr> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_ShowTickets">Calculate Winnings</Button>
<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Quest TeleportToRaceTrack" msg="1298;Monster Race Track">Exit the Monster Race Track</Button></td></tr> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_ViewHistory">View Past Results</Button>
</table> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Quest TeleportToRaceTrack" msg="1298;Monster Race Track">Exit the monster race track.</Button>
<img src="L2UI.SquareBlank" width=270 height=10> <img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareBlank" width=270 height=5> <img src="L2UI.SquareBlank" width=270 height=10>
<img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=270 height=5>
</body></html> </body></html>

View File

@@ -105,7 +105,6 @@ import handlers.admincommandhandlers.AdminMenu;
import handlers.admincommandhandlers.AdminMessages; import handlers.admincommandhandlers.AdminMessages;
import handlers.admincommandhandlers.AdminMissingHtmls; import handlers.admincommandhandlers.AdminMissingHtmls;
import handlers.admincommandhandlers.AdminMobGroup; import handlers.admincommandhandlers.AdminMobGroup;
import handlers.admincommandhandlers.AdminMonsterRace;
import handlers.admincommandhandlers.AdminOlympiad; import handlers.admincommandhandlers.AdminOlympiad;
import handlers.admincommandhandlers.AdminOnline; import handlers.admincommandhandlers.AdminOnline;
import handlers.admincommandhandlers.AdminPForge; import handlers.admincommandhandlers.AdminPForge;
@@ -441,7 +440,6 @@ public class MasterHandler
AdminMessages.class, AdminMessages.class,
AdminMissingHtmls.class, AdminMissingHtmls.class,
AdminMobGroup.class, AdminMobGroup.class,
AdminMonsterRace.class,
AdminOlympiad.class, AdminOlympiad.class,
AdminOnline.class, AdminOnline.class,
AdminPathNode.class, AdminPathNode.class,

View File

@@ -1,166 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package handlers.admincommandhandlers;
import org.l2jmobius.commons.concurrent.ThreadPool;
import org.l2jmobius.gameserver.MonsterRace;
import org.l2jmobius.gameserver.handler.IAdminCommandHandler;
import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
import org.l2jmobius.gameserver.network.SystemMessageId;
import org.l2jmobius.gameserver.network.serverpackets.DeleteObject;
import org.l2jmobius.gameserver.network.serverpackets.MonRaceInfo;
import org.l2jmobius.gameserver.network.serverpackets.PlaySound;
import org.l2jmobius.gameserver.network.serverpackets.SystemMessage;
/**
* This class handles following admin commands: - invul = turns invulnerability on/off
* @version $Revision: 1.1.6.4 $ $Date: 2007/07/31 10:06:00 $
*/
public class AdminMonsterRace implements IAdminCommandHandler
{
private static final String[] ADMIN_COMMANDS =
{
"admin_mons"
};
static int state = -1;
@Override
public boolean useAdminCommand(String command, PlayerInstance activeChar)
{
if (command.equalsIgnoreCase("admin_mons"))
{
handleSendPacket(activeChar);
}
return true;
}
@Override
public String[] getAdminCommandList()
{
return ADMIN_COMMANDS;
}
private void handleSendPacket(PlayerInstance activeChar)
{
/*
* -1 0 to initialize the race 0 15322 to start race 13765 -1 in middle of race -1 0 to end the race 8003 to 8027
*/
final int[][] codes =
{
{
-1,
0
},
{
0,
15322
},
{
13765,
-1
},
{
-1,
0
}
};
final MonsterRace race = MonsterRace.getInstance();
if (state == -1)
{
state++;
race.newRace();
race.newSpeeds();
final MonRaceInfo spk = new MonRaceInfo(codes[state][0], codes[state][1], race.getMonsters(), race.getSpeeds());
activeChar.sendPacket(spk);
activeChar.broadcastPacket(spk);
}
else if (state == 0)
{
state++;
final SystemMessage sm = new SystemMessage(SystemMessageId.THEY_RE_OFF);
sm.addInt(0);
activeChar.sendPacket(sm);
final PlaySound sRace = new PlaySound(1, "S_Race", 0, 0, 0, 0, 0);
activeChar.sendPacket(sRace);
activeChar.broadcastPacket(sRace);
final PlaySound sRace2 = new PlaySound(0, "ItemSound2.race_start", 1, 121209259, 12125, 182487, -3559);
activeChar.sendPacket(sRace2);
activeChar.broadcastPacket(sRace2);
final MonRaceInfo spk = new MonRaceInfo(codes[state][0], codes[state][1], race.getMonsters(), race.getSpeeds());
activeChar.sendPacket(spk);
activeChar.broadcastPacket(spk);
ThreadPool.schedule(new RunRace(codes, activeChar), 5000);
}
}
private class RunRace implements Runnable
{
private final int[][] codes;
private final PlayerInstance activeChar;
public RunRace(int[][] pCodes, PlayerInstance pActiveChar)
{
codes = pCodes;
activeChar = pActiveChar;
}
@Override
public void run()
{
// int[][] speeds1 = MonsterRace.getInstance().getSpeeds();
// MonsterRace.getInstance().newSpeeds();
// int[][] speeds2 = MonsterRace.getInstance().getSpeeds();
/*
* int[] speed = new int[8]; for (int i=0; i<8; i++) { for (int j=0; j<20; j++) { //LOGGER.info("Adding "+speeds1[i][j] +" and "+ speeds2[i][j]); speed[i] += (speeds1[i][j]*1); // + (speeds2[i][j]*1); } LOGGER.info("Total speed for "+(i+1)+" = "+speed[i]); }
*/
final MonRaceInfo spk = new MonRaceInfo(codes[2][0], codes[2][1], MonsterRace.getInstance().getMonsters(), MonsterRace.getInstance().getSpeeds());
activeChar.sendPacket(spk);
activeChar.broadcastPacket(spk);
ThreadPool.schedule(new RunEnd(activeChar), 30000);
}
}
private static class RunEnd implements Runnable
{
private final PlayerInstance activeChar;
public RunEnd(PlayerInstance pActiveChar)
{
activeChar = pActiveChar;
}
@Override
public void run()
{
DeleteObject obj = null;
for (int i = 0; i < 8; i++)
{
obj = new DeleteObject(MonsterRace.getInstance().getMonsters()[i]);
activeChar.sendPacket(obj);
activeChar.broadcastPacket(obj);
}
state = -1;
}
}
}

View File

@@ -144,6 +144,7 @@ import org.l2jmobius.gameserver.instancemanager.SiegeGuardManager;
import org.l2jmobius.gameserver.instancemanager.SiegeManager; import org.l2jmobius.gameserver.instancemanager.SiegeManager;
import org.l2jmobius.gameserver.instancemanager.WalkingManager; import org.l2jmobius.gameserver.instancemanager.WalkingManager;
import org.l2jmobius.gameserver.instancemanager.ZoneManager; import org.l2jmobius.gameserver.instancemanager.ZoneManager;
import org.l2jmobius.gameserver.instancemanager.games.MonsterRace;
import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.World;
import org.l2jmobius.gameserver.model.entity.Hero; import org.l2jmobius.gameserver.model.entity.Hero;
import org.l2jmobius.gameserver.model.events.EventDispatcher; import org.l2jmobius.gameserver.model.events.EventDispatcher;
@@ -404,10 +405,8 @@ public class GameServer
ItemsAutoDestroy.getInstance(); ItemsAutoDestroy.getInstance();
} }
if (Config.ALLOW_RACE)
{
MonsterRace.getInstance(); MonsterRace.getInstance();
}
TaskManager.getInstance(); TaskManager.getInstance();
AntiFeedManager.getInstance().registerEvent(AntiFeedManager.GAME_ID); AntiFeedManager.getInstance().registerEvent(AntiFeedManager.GAME_ID);

View File

@@ -1,140 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.data.xml.impl.NpcData;
import org.l2jmobius.gameserver.model.actor.Npc;
import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate;
public class MonsterRace
{
protected static final Logger LOGGER = Logger.getLogger(MonsterRace.class.getName());
private final Npc[] _monsters;
private int[][] _speeds;
private final int[] _first;
private final int[] _second;
protected MonsterRace()
{
_monsters = new Npc[8];
_speeds = new int[8][20];
_first = new int[2];
_second = new int[2];
}
public void newRace()
{
int random = 0;
for (int i = 0; i < 8; i++)
{
final int id = 31003;
random = Rnd.get(24);
while (true)
{
for (int j = i - 1; j >= 0; j--)
{
if (_monsters[j].getTemplate().getId() == (id + random))
{
random = Rnd.get(24);
}
}
break;
}
try
{
final NpcTemplate template = NpcData.getInstance().getTemplate(id + random);
_monsters[i] = (Npc) Class.forName("org.l2jmobius.gameserver.model.actor.instance." + template.getType() + "Instance").getConstructors()[0].newInstance(template);
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Unable to create monster!", e);
}
}
newSpeeds();
}
public void newSpeeds()
{
_speeds = new int[8][20];
int total = 0;
_first[1] = 0;
_second[1] = 0;
for (int i = 0; i < 8; i++)
{
total = 0;
for (int j = 0; j < 20; j++)
{
_speeds[i][j] = j == 19 ? 100 : Rnd.get(60) + 65;
total += _speeds[i][j];
}
if (total >= _first[1])
{
_second[0] = _first[0];
_second[1] = _first[1];
_first[0] = 8 - i;
_first[1] = total;
}
else if (total >= _second[1])
{
_second[0] = 8 - i;
_second[1] = total;
}
}
}
/**
* @return Returns the monsters.
*/
public Npc[] getMonsters()
{
return _monsters;
}
/**
* @return Returns the speeds.
*/
public int[][] getSpeeds()
{
return _speeds;
}
public int getFirstPlace()
{
return _first[0];
}
public int getSecondPlace()
{
return _second[0];
}
public static MonsterRace getInstance()
{
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder
{
protected static final MonsterRace INSTANCE = new MonsterRace();
}
}

View File

@@ -0,0 +1,623 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.instancemanager.games;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.l2jmobius.Config;
import org.l2jmobius.commons.concurrent.ThreadPool;
import org.l2jmobius.commons.database.DatabaseFactory;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.data.xml.impl.NpcData;
import org.l2jmobius.gameserver.model.actor.Npc;
import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate;
import org.l2jmobius.gameserver.model.zone.type.DerbyTrackZone;
import org.l2jmobius.gameserver.network.SystemMessageId;
import org.l2jmobius.gameserver.network.serverpackets.DeleteObject;
import org.l2jmobius.gameserver.network.serverpackets.MonRaceInfo;
import org.l2jmobius.gameserver.network.serverpackets.PlaySound;
import org.l2jmobius.gameserver.network.serverpackets.SystemMessage;
import org.l2jmobius.gameserver.util.Broadcast;
public class MonsterRace
{
protected static final Logger LOGGER = Logger.getLogger(MonsterRace.class.getName());
public static enum RaceState
{
ACCEPTING_BETS,
WAITING,
STARTING_RACE,
RACE_END
}
protected static final PlaySound SOUND_1 = new PlaySound(1, "S_Race", 0, 0, 0, 0, 0);
protected static final PlaySound SOUND_2 = new PlaySound("ItemSound2.race_start");
protected static final int[][] CODES =
{
{
-1,
0
},
{
0,
15322
},
{
13765,
-1
}
};
protected final List<Integer> _npcTemplates = new ArrayList<>(); // List holding npc templates, shuffled on a new race.
protected final List<HistoryInfo> _history = new ArrayList<>(); // List holding old race records.
protected final Map<Integer, Long> _betsPerLane = new ConcurrentHashMap<>(); // Map holding all bets for each lane ; values setted to 0 after every race.
protected final List<Double> _odds = new ArrayList<>(); // List holding sorted odds per lane ; cleared at new odds calculation.
protected int _raceNumber = 1;
protected int _finalCountdown = 0;
protected RaceState _state = RaceState.RACE_END;
protected MonRaceInfo _packet;
private final Npc[] _monsters = new Npc[8];
private int[][] _speeds = new int[8][20];
private final int[] _first = new int[2];
private final int[] _second = new int[2];
protected MonsterRace()
{
if (!Config.ALLOW_RACE)
{
return;
}
// Feed _history with previous race results.
loadHistory();
// Feed _betsPerLane with stored informations on bets.
loadBets();
// Feed _npcTemplates, we will only have to shuffle it when needed.
for (int i = 31003; i < 31027; i++)
{
_npcTemplates.add(i);
}
ThreadPool.scheduleAtFixedRate(new Announcement(), 0, 1000);
}
public static class HistoryInfo
{
private final int _raceId;
private int _first;
private int _second;
private double _oddRate;
public HistoryInfo(int raceId, int first, int second, double oddRate)
{
_raceId = raceId;
_first = first;
_second = second;
_oddRate = oddRate;
}
public int getRaceId()
{
return _raceId;
}
public int getFirst()
{
return _first;
}
public int getSecond()
{
return _second;
}
public double getOddRate()
{
return _oddRate;
}
public void setFirst(int first)
{
_first = first;
}
public void setSecond(int second)
{
_second = second;
}
public void setOddRate(double oddRate)
{
_oddRate = oddRate;
}
}
private class Announcement implements Runnable
{
public Announcement()
{
}
@Override
public void run()
{
if (_finalCountdown > 1200)
{
_finalCountdown = 0;
}
switch (_finalCountdown)
{
case 0:
{
newRace();
newSpeeds();
_state = RaceState.ACCEPTING_BETS;
_packet = new MonRaceInfo(CODES[0][0], CODES[0][1], getMonsters(), getSpeeds());
final SystemMessage msg = new SystemMessage(SystemMessageId.TICKETS_ARE_NOW_AVAILABLE_FOR_MONSTER_RACE_S1);
msg.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, _packet, msg);
break;
}
case 30: // 30 sec
case 60: // 1 min
case 90: // 1 min 30 sec
case 120: // 2 min
case 150: // 2 min 30
case 180: // 3 min
case 210: // 3 min 30
case 240: // 4 min
case 270: // 4 min 30 sec
case 330: // 5 min 30 sec
case 360: // 6 min
case 390: // 6 min 30 sec
case 420: // 7 min
case 450: // 7 min 30
case 480: // 8 min
case 510: // 8 min 30
case 540: // 9 min
case 570: // 9 min 30 sec
case 630: // 10 min 30 sec
case 660: // 11 min
case 690: // 11 min 30 sec
case 720: // 12 min
case 750: // 12 min 30
case 780: // 13 min
case 810: // 13 min 30
case 870: // 14 min 30 sec
{
final SystemMessage msg = new SystemMessage(SystemMessageId.TICKETS_ARE_NOW_AVAILABLE_FOR_MONSTER_RACE_S1);
msg.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg);
break;
}
case 300: // 5 min
{
final SystemMessage msg = new SystemMessage(SystemMessageId.NOW_SELLING_TICKETS_FOR_MONSTER_RACE_S1);
msg.addInt(_raceNumber);
final SystemMessage msg2 = new SystemMessage(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S);
msg2.addInt(10);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg, msg2);
break;
}
case 600: // 10 min
{
final SystemMessage msg = new SystemMessage(SystemMessageId.NOW_SELLING_TICKETS_FOR_MONSTER_RACE_S1);
msg.addInt(_raceNumber);
final SystemMessage msg2 = new SystemMessage(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S);
msg2.addInt(5);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg, msg2);
break;
}
case 840: // 14 min
{
final SystemMessage msg = new SystemMessage(SystemMessageId.NOW_SELLING_TICKETS_FOR_MONSTER_RACE_S1);
msg.addInt(_raceNumber);
final SystemMessage msg2 = new SystemMessage(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S);
msg2.addInt(1);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg, msg2);
break;
}
case 900: // 15 min
{
_state = RaceState.WAITING;
calculateOdds();
final SystemMessage msg = new SystemMessage(SystemMessageId.TICKETS_ARE_NOW_AVAILABLE_FOR_MONSTER_RACE_S1);
msg.addInt(_raceNumber);
final SystemMessage msg2 = new SystemMessage(SystemMessageId.TICKETS_SALES_ARE_CLOSED_FOR_MONSTER_RACE_S1_ODDS_ARE_POSTED);
msg2.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg, msg2);
break;
}
case 960: // 16 min
case 1020: // 17 min
{
final int minutes = (_finalCountdown == 960) ? 2 : 1;
final SystemMessage msg = new SystemMessage(SystemMessageId.MONSTER_RACE_S2_WILL_BEGIN_IN_S1_MINUTE_S);
msg.addInt(minutes);
msg.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg);
break;
}
case 1050: // 17 min 30 sec
{
final SystemMessage msg = new SystemMessage(SystemMessageId.MONSTER_RACE_S1_WILL_BEGIN_IN_30_SECONDS);
msg.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg);
break;
}
case 1070: // 17 min 50 sec
{
final SystemMessage msg = new SystemMessage(SystemMessageId.MONSTER_RACE_S1_IS_ABOUT_TO_BEGIN_COUNTDOWN_IN_FIVE_SECONDS);
msg.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg);
break;
}
case 1075: // 17 min 55 sec
case 1076: // 17 min 56 sec
case 1077: // 17 min 57 sec
case 1078: // 17 min 58 sec
case 1079: // 17 min 59 sec
{
final int seconds = 1080 - _finalCountdown;
final SystemMessage msg = new SystemMessage(SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SECOND_S);
msg.addInt(seconds);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg);
break;
}
case 1080: // 18 min
{
_state = RaceState.STARTING_RACE;
_packet = new MonRaceInfo(CODES[1][0], CODES[1][1], getMonsters(), getSpeeds());
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, new SystemMessage(SystemMessageId.THEY_RE_OFF), SOUND_1, SOUND_2, _packet);
break;
}
case 1085: // 18 min 5 sec
{
_packet = new MonRaceInfo(CODES[2][0], CODES[2][1], getMonsters(), getSpeeds());
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, _packet);
break;
}
case 1115: // 18 min 35 sec
{
_state = RaceState.RACE_END;
// Populate history info with data, stores it in database.
final HistoryInfo info = _history.get(_history.size() - 1);
info.setFirst(getFirstPlace());
info.setSecond(getSecondPlace());
info.setOddRate(_odds.get(getFirstPlace() - 1));
saveHistory(info);
clearBets();
final SystemMessage msg = new SystemMessage(SystemMessageId.FIRST_PRIZE_GOES_TO_THE_PLAYER_IN_LANE_S1_SECOND_PRIZE_GOES_TO_THE_PLAYER_IN_LANE_S2);
msg.addInt(getFirstPlace());
msg.addInt(getSecondPlace());
final SystemMessage msg2 = new SystemMessage(SystemMessageId.MONSTER_RACE_S1_IS_FINISHED);
msg2.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg, msg2);
_raceNumber++;
break;
}
case 1140: // 19 min
{
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, new DeleteObject(getMonsters()[0]), new DeleteObject(getMonsters()[1]), new DeleteObject(getMonsters()[2]), new DeleteObject(getMonsters()[3]), new DeleteObject(getMonsters()[4]), new DeleteObject(getMonsters()[5]), new DeleteObject(getMonsters()[6]), new DeleteObject(getMonsters()[7]));
break;
}
}
_finalCountdown += 1;
}
}
public void newRace()
{
// Edit _history.
_history.add(new HistoryInfo(_raceNumber, 0, 0, 0));
// Randomize _npcTemplates.
Collections.shuffle(_npcTemplates);
// Setup 8 new creatures ; pickup the first 8 from _npcTemplates.
for (int i = 0; i < 8; i++)
{
try
{
final NpcTemplate template = NpcData.getInstance().getTemplate(_npcTemplates.get(i));
_monsters[i] = (Npc) Class.forName("org.l2jmobius.gameserver.model.actor.instance." + template.getType() + "Instance").getConstructors()[0].newInstance(template);
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "", e);
}
}
}
public void newSpeeds()
{
_speeds = new int[8][20];
int total = 0;
_first[1] = 0;
_second[1] = 0;
for (int i = 0; i < 8; i++)
{
total = 0;
for (int j = 0; j < 20; j++)
{
if (j == 19)
{
_speeds[i][j] = 100;
}
else
{
_speeds[i][j] = Rnd.get(60) + 65;
}
total += _speeds[i][j];
}
if (total >= _first[1])
{
_second[0] = _first[0];
_second[1] = _first[1];
_first[0] = 8 - i;
_first[1] = total;
}
else if (total >= _second[1])
{
_second[0] = 8 - i;
_second[1] = total;
}
}
}
/**
* Load past races informations, feeding _history arrayList.<br>
* Also sets _raceNumber, based on latest HistoryInfo loaded.
*/
protected void loadHistory()
{
try (Connection con = DatabaseFactory.getConnection())
{
PreparedStatement statement = con.prepareStatement("SELECT * FROM mdt_history");
ResultSet rset = statement.executeQuery();
while (rset.next())
{
_history.add(new HistoryInfo(rset.getInt("race_id"), rset.getInt("first"), rset.getInt("second"), rset.getDouble("odd_rate")));
_raceNumber++;
}
rset.close();
statement.close();
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, "MonsterRace: Can't load history: " + e.getMessage(), e);
}
LOGGER.info("MonsterRace: loaded " + _history.size() + " records, currently on race #" + _raceNumber);
}
/**
* Save an history record into database.
* @param history The infos to store.
*/
protected void saveHistory(HistoryInfo history)
{
try (Connection con = DatabaseFactory.getConnection())
{
PreparedStatement statement = con.prepareStatement("INSERT INTO mdt_history (race_id, first, second, odd_rate) VALUES (?,?,?,?)");
statement.setInt(1, history.getRaceId());
statement.setInt(2, history.getFirst());
statement.setInt(3, history.getSecond());
statement.setDouble(4, history.getOddRate());
statement.execute();
statement.close();
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, "MonsterRace: Can't save history: " + e.getMessage(), e);
}
}
/**
* Load current bets per lane ; initialize the map keys.
*/
protected void loadBets()
{
try (Connection con = DatabaseFactory.getConnection())
{
PreparedStatement statement = con.prepareStatement("SELECT * FROM mdt_bets");
ResultSet rset = statement.executeQuery();
while (rset.next())
{
setBetOnLane(rset.getInt("lane_id"), rset.getLong("bet"), false);
}
rset.close();
statement.close();
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, "MonsterRace: Can't load bets: " + e.getMessage(), e);
}
}
/**
* Save the current lane bet into database.
* @param lane : The lane to affect.
* @param sum : The sum to set.
*/
protected void saveBet(int lane, long sum)
{
try (Connection con = DatabaseFactory.getConnection())
{
PreparedStatement statement = con.prepareStatement("REPLACE INTO mdt_bets (lane_id, bet) VALUES (?,?)");
statement.setInt(1, lane);
statement.setLong(2, sum);
statement.execute();
statement.close();
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, "MonsterRace: Can't save bet: " + e.getMessage(), e);
}
}
/**
* Clear all lanes bets, either on database or Map.
*/
protected void clearBets()
{
for (int key : _betsPerLane.keySet())
{
_betsPerLane.put(key, 0L);
}
try (Connection con = DatabaseFactory.getConnection())
{
PreparedStatement statement = con.prepareStatement("UPDATE mdt_bets SET bet = 0");
statement.execute();
statement.close();
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, "MonsterRace: Can't clear bets: " + e.getMessage(), e);
}
}
/**
* Setup lane bet, based on previous value (if any).
* @param lane : The lane to edit.
* @param amount : The amount to add.
* @param saveOnDb : Should it be saved on db or not.
*/
public void setBetOnLane(int lane, long amount, boolean saveOnDb)
{
final long sum = (_betsPerLane.containsKey(lane)) ? _betsPerLane.get(lane) + amount : amount;
_betsPerLane.put(lane, sum);
if (saveOnDb)
{
saveBet(lane, sum);
}
}
/**
* Calculate odds for every lane, based on others lanes.
*/
protected void calculateOdds()
{
// Clear previous List holding old odds.
_odds.clear();
// Sort bets lanes per lane.
final Map<Integer, Long> sortedLanes = new TreeMap<>(_betsPerLane);
// Pass a first loop in order to calculate total sum of all lanes.
long sumOfAllLanes = 0;
for (long amount : sortedLanes.values())
{
sumOfAllLanes += amount;
}
// As we get the sum, we can now calculate the odd rate of each lane.
for (long amount : sortedLanes.values())
{
_odds.add((amount == 0) ? 0D : Math.max(1.25, (sumOfAllLanes * 0.7) / amount));
}
}
public Npc[] getMonsters()
{
return _monsters;
}
public int[][] getSpeeds()
{
return _speeds;
}
public int getFirstPlace()
{
return _first[0];
}
public int getSecondPlace()
{
return _second[0];
}
public MonRaceInfo getRacePacket()
{
return _packet;
}
public RaceState getCurrentRaceState()
{
return _state;
}
public int getRaceNumber()
{
return _raceNumber;
}
public List<HistoryInfo> getHistory()
{
return _history;
}
public List<Double> getOdds()
{
return _odds;
}
public static MonsterRace getInstance()
{
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder
{
protected static final MonsterRace INSTANCE = new MonsterRace();
}
}

View File

@@ -16,69 +16,26 @@
*/ */
package org.l2jmobius.gameserver.model.actor.instance; package org.l2jmobius.gameserver.model.actor.instance;
import java.util.Collection; import java.util.List;
import java.util.concurrent.ConcurrentHashMap; import java.util.Locale;
import org.l2jmobius.Config; import org.l2jmobius.Config;
import org.l2jmobius.commons.concurrent.ThreadPool; import org.l2jmobius.commons.util.StringUtil;
import org.l2jmobius.gameserver.MonsterRace;
import org.l2jmobius.gameserver.enums.InstanceType;
import org.l2jmobius.gameserver.idfactory.IdFactory; import org.l2jmobius.gameserver.idfactory.IdFactory;
import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.instancemanager.games.MonsterRace;
import org.l2jmobius.gameserver.instancemanager.games.MonsterRace.HistoryInfo;
import org.l2jmobius.gameserver.instancemanager.games.MonsterRace.RaceState;
import org.l2jmobius.gameserver.model.actor.Npc; import org.l2jmobius.gameserver.model.actor.Npc;
import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate; import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate;
import org.l2jmobius.gameserver.model.itemcontainer.Inventory;
import org.l2jmobius.gameserver.model.items.instance.ItemInstance; import org.l2jmobius.gameserver.model.items.instance.ItemInstance;
import org.l2jmobius.gameserver.network.SystemMessageId; import org.l2jmobius.gameserver.network.SystemMessageId;
import org.l2jmobius.gameserver.network.serverpackets.ActionFailed; import org.l2jmobius.gameserver.network.serverpackets.ActionFailed;
import org.l2jmobius.gameserver.network.serverpackets.DeleteObject;
import org.l2jmobius.gameserver.network.serverpackets.IClientOutgoingPacket;
import org.l2jmobius.gameserver.network.serverpackets.InventoryUpdate;
import org.l2jmobius.gameserver.network.serverpackets.MonRaceInfo;
import org.l2jmobius.gameserver.network.serverpackets.NpcHtmlMessage; import org.l2jmobius.gameserver.network.serverpackets.NpcHtmlMessage;
import org.l2jmobius.gameserver.network.serverpackets.PlaySound;
import org.l2jmobius.gameserver.network.serverpackets.SystemMessage; import org.l2jmobius.gameserver.network.serverpackets.SystemMessage;
import org.l2jmobius.gameserver.util.Broadcast;
public class RaceManagerInstance extends Npc public class RaceManagerInstance extends Npc
{ {
public static final int LANES = 8; protected static final int TICKET_PRICES[] =
public static final int WINDOW_START = 0;
private static Collection<RaceManagerInstance> _managers;
protected static int _raceNumber = 4;
// Time Constants
private static final long SECOND = 1000;
private static final long MINUTE = 60 * SECOND;
private static int _minutes = 5;
// States
private static final int ACCEPTING_BETS = 0;
private static final int WAITING = 1;
private static final int STARTING_RACE = 2;
private static final int RACE_END = 3;
private static int _state = RACE_END;
protected static final int[][] _codes =
{
{
-1,
0
},
{
0,
15322
},
{
13765,
-1
}
};
private static boolean _notInitialized = true;
protected static MonRaceInfo _packet;
protected static final int[] _cost =
{ {
100, 100,
500, 500,
@@ -93,223 +50,167 @@ public class RaceManagerInstance extends Npc
public RaceManagerInstance(NpcTemplate template) public RaceManagerInstance(NpcTemplate template)
{ {
super(template); super(template);
setInstanceType(InstanceType.RaceManagerInstance);
if (!Config.ALLOW_RACE)
{
return;
}
if (_notInitialized)
{
_notInitialized = false;
_managers = ConcurrentHashMap.newKeySet();
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKETS_ARE_NOW_AVAILABLE_FOR_MONSTER_RACE_S1), 0, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.NOW_SELLING_TICKETS_FOR_MONSTER_RACE_S1), 30 * SECOND, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKETS_ARE_NOW_AVAILABLE_FOR_MONSTER_RACE_S1), MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.NOW_SELLING_TICKETS_FOR_MONSTER_RACE_S1), MINUTE + (30 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S), 2 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S), 3 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S), 4 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S), 5 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S), 6 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKETS_SALES_ARE_CLOSED_FOR_MONSTER_RACE_S1_ODDS_ARE_POSTED), 7 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.MONSTER_RACE_S2_WILL_BEGIN_IN_S1_MINUTE_S), 7 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.MONSTER_RACE_S2_WILL_BEGIN_IN_S1_MINUTE_S), 8 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.MONSTER_RACE_S1_WILL_BEGIN_IN_30_SECONDS), (8 * MINUTE) + (30 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.MONSTER_RACE_S1_IS_ABOUT_TO_BEGIN_COUNTDOWN_IN_FIVE_SECONDS), (8 * MINUTE) + (50 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SECOND_S), (8 * MINUTE) + (55 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SECOND_S), (8 * MINUTE) + (56 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SECOND_S), (8 * MINUTE) + (57 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SECOND_S), (8 * MINUTE) + (58 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SECOND_S), (8 * MINUTE) + (59 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.THEY_RE_OFF), 9 * MINUTE, 10 * MINUTE);
}
_managers.add(this);
}
@Override
public boolean isAutoAttackable(Creature attacker)
{
if (attacker.isMonster())
{
return true;
}
return super.isAutoAttackable(attacker);
}
class Announcement implements Runnable
{
private final SystemMessageId _type;
public Announcement(SystemMessageId pType)
{
_type = pType;
}
@Override
public void run()
{
makeAnnouncement(_type);
}
}
public void makeAnnouncement(SystemMessageId type)
{
final SystemMessage sm = new SystemMessage(type);
switch (type.getId())
{
case 816: // SystemMessageId.TICKETS_ARE_NOW_AVAILABLE_FOR_MONSTER_RACE_S1
case 817: // SystemMessageId.NOW_SELLING_TICKETS_FOR_MONSTER_RACE_S1
{
if (_state != ACCEPTING_BETS)
{// LOGGER.info("Race Initializing");
_state = ACCEPTING_BETS;
startRace();
} // else{LOGGER.info("Race open");}
sm.addInt(_raceNumber);
break;
}
case 818: // SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S
case 820: // SystemMessageId.MONSTER_RACE_S2_WILL_BEGIN_IN_S1_MINUTE_S
case 823: // SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SECOND_S
{
sm.addInt(_minutes);
if (type.getId() == 820)
{
sm.addInt(_raceNumber);
}
_minutes--;
break;
}
case 819: // SystemMessageId.TICKETS_SALES_ARE_CLOSED_FOR_MONSTER_RACE_S1_ODDS_ARE_POSTED
{
// LOGGER.info("Sales closed");
sm.addInt(_raceNumber);
_state = WAITING;
_minutes = 2;
break;
}
case 821: // SystemMessageId.MONSTER_RACE_S1_WILL_BEGIN_IN_30_SECONDS
case 822: // SystemMessageId.MONSTER_RACE_S1_IS_ABOUT_TO_BEGIN_COUNTDOWN_IN_FIVE_SECONDS
case 825: // SystemMessageId.MONSTER_RACE_S1_IS_FINISHED
{
sm.addInt(_raceNumber);
_minutes = 5;
break;
}
case 826: // SystemMessageId.FIRST_PRIZE_GOES_TO_THE_PLAYER_IN_LANE_S1_SECOND_PRIZE_GOES_TO_THE_PLAYER_IN_LANE_S2
{
// LOGGER.info("Placing");
_state = RACE_END;
sm.addInt(MonsterRace.getInstance().getFirstPlace());
sm.addInt(MonsterRace.getInstance().getSecondPlace());
break;
}
}
// _logn.info("Counter: "+minutes);
// LOGGER.info("State: "+state);
broadcast(sm);
// LOGGER.info("Player's known: "+getKnownPlayers().size());
if (type == SystemMessageId.THEY_RE_OFF)
{
// LOGGER.info("Starting race");
_state = STARTING_RACE;
startRace();
_minutes = 5;
}
}
protected void broadcast(IClientOutgoingPacket pkt)
{
for (RaceManagerInstance manager : _managers)
{
if (!manager.isDead())
{
Broadcast.toKnownPlayers(manager, pkt);
}
}
}
public void sendMonsterInfo()
{
broadcast(_packet);
}
private void startRace()
{
final MonsterRace race = MonsterRace.getInstance();
if (_state == STARTING_RACE)
{
// state++;
broadcast(new PlaySound(1, "S_Race", 0, 0, 0, 0, 0));
broadcast(new PlaySound(0, "ItemSound2.race_start", 1, 121209259, 12125, 182487, -3559));
_packet = new MonRaceInfo(_codes[1][0], _codes[1][1], race.getMonsters(), race.getSpeeds());
sendMonsterInfo();
ThreadPool.schedule(new RunRace(), 5000);
}
else
{
// state++;
race.newRace();
race.newSpeeds();
_packet = new MonRaceInfo(_codes[0][0], _codes[0][1], race.getMonsters(), race.getSpeeds());
sendMonsterInfo();
}
} }
@Override @Override
public void onBypassFeedback(PlayerInstance player, String command) public void onBypassFeedback(PlayerInstance player, String command)
{ {
if (command.startsWith("BuyTicket") && (_state != ACCEPTING_BETS))
{
if (!Config.ALLOW_RACE)
{
return;
}
player.sendPacket(SystemMessageId.MONSTER_RACE_TICKETS_ARE_NO_LONGER_AVAILABLE);
command = "Chat 0";
}
if (command.startsWith("ShowOdds") && (_state == ACCEPTING_BETS))
{
if (!Config.ALLOW_RACE)
{
return;
}
player.sendPacket(SystemMessageId.MONSTER_RACE_PAYOUT_INFORMATION_IS_NOT_AVAILABLE_WHILE_TICKETS_ARE_BEING_SOLD);
command = "Chat 0";
}
if (command.startsWith("BuyTicket")) if (command.startsWith("BuyTicket"))
{ {
if (!Config.ALLOW_RACE) if (!Config.ALLOW_RACE || (MonsterRace.getInstance().getCurrentRaceState() != RaceState.ACCEPTING_BETS))
{ {
player.sendPacket(SystemMessageId.MONSTER_RACE_TICKETS_ARE_NO_LONGER_AVAILABLE);
super.onBypassFeedback(player, "Chat 0");
return; return;
} }
int val = Integer.parseInt(command.substring(10)); int val = Integer.parseInt(command.substring(10));
if (val == 0) if (val == 0)
{ {
player.setRace(0, 0); player.setRace(0, 0);
player.setRace(1, 0); player.setRace(1, 0);
} }
if (((val == 10) && (player.getRace(0) == 0)) || ((val == 20) && (player.getRace(0) == 0) && (player.getRace(1) == 0))) if (((val == 10) && (player.getRace(0) == 0)) || ((val == 20) && (player.getRace(0) == 0) && (player.getRace(1) == 0)))
{ {
val = 0; val = 0;
} }
showBuyTicket(player, val);
} String search, replace;
else if (command.equals("ShowOdds"))
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
if (val < 10)
{ {
if (!Config.ALLOW_RACE) html.setFile(player, getHtmlPath(getId(), 2, player));
for (int i = 0; i < 8; i++)
{
int n = i + 1;
search = "Mob" + n;
html.replace(search, MonsterRace.getInstance().getMonsters()[i].getTemplate().getName());
}
search = "No1";
if (val == 0)
{
html.replace(search, "");
}
else
{
html.replace(search, val);
player.setRace(0, val);
}
}
else if (val < 20)
{
if (player.getRace(0) == 0)
{ {
return; return;
} }
showOdds(player);
html.setFile(player, getHtmlPath(getId(), 3, player));
html.replace("0place", player.getRace(0));
search = "Mob1";
replace = MonsterRace.getInstance().getMonsters()[player.getRace(0) - 1].getTemplate().getName();
html.replace(search, replace);
search = "0adena";
if (val == 10)
{
html.replace(search, "");
}
else
{
html.replace(search, TICKET_PRICES[val - 11]);
player.setRace(1, val - 10);
}
}
else if (val == 20)
{
if ((player.getRace(0) == 0) || (player.getRace(1) == 0))
{
return;
}
html.setFile(player, getHtmlPath(getId(), 4, player));
html.replace("0place", player.getRace(0));
search = "Mob1";
replace = MonsterRace.getInstance().getMonsters()[player.getRace(0) - 1].getTemplate().getName();
html.replace(search, replace);
search = "0adena";
int price = TICKET_PRICES[player.getRace(1) - 1];
html.replace(search, price);
search = "0tax";
int tax = 0;
html.replace(search, tax);
search = "0total";
int total = price + tax;
html.replace(search, total);
}
else
{
if ((player.getRace(0) == 0) || (player.getRace(1) == 0))
{
return;
}
int ticket = player.getRace(0);
int priceId = player.getRace(1);
if (!player.reduceAdena("Race", TICKET_PRICES[priceId - 1], this, true))
{
return;
}
player.setRace(0, 0);
player.setRace(1, 0);
ItemInstance item = new ItemInstance(IdFactory.getInstance().getNextId(), 4443);
item.setCount(1);
item.setEnchantLevel(MonsterRace.getInstance().getRaceNumber());
item.setCustomType1(ticket);
item.setCustomType2(TICKET_PRICES[priceId - 1] / 100);
player.addItem("Race", item, player, false);
final SystemMessage msg = new SystemMessage(SystemMessageId.ACQUIRED_S1_S2);
msg.addInt(MonsterRace.getInstance().getRaceNumber());
msg.addItemName(4443);
player.sendPacket(msg);
// Refresh lane bet.
MonsterRace.getInstance().setBetOnLane(ticket, TICKET_PRICES[priceId - 1], true);
super.onBypassFeedback(player, "Chat 0");
return;
}
html.replace("1race", MonsterRace.getInstance().getRaceNumber());
html.replace("%objectId%", getObjectId());
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
else if (command.equals("ShowOdds"))
{
if (!Config.ALLOW_RACE || (MonsterRace.getInstance().getCurrentRaceState() == RaceState.ACCEPTING_BETS))
{
player.sendPacket(SystemMessageId.MONSTER_RACE_PAYOUT_INFORMATION_IS_NOT_AVAILABLE_WHILE_TICKETS_ARE_BEING_SOLD);
super.onBypassFeedback(player, "Chat 0");
return;
}
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
html.setFile(player, getHtmlPath(getId(), 5, player));
for (int i = 0; i < 8; i++)
{
final int n = i + 1;
html.replace("Mob" + n, MonsterRace.getInstance().getMonsters()[i].getTemplate().getName());
// Odd
final double odd = MonsterRace.getInstance().getOdds().get(i);
html.replace("Odd" + n, (odd > 0D) ? String.format(Locale.ENGLISH, "%.1f", odd) : "&$804;");
}
html.replace("1race", MonsterRace.getInstance().getRaceNumber());
html.replace("%objectId%", getObjectId());
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
} }
else if (command.equals("ShowInfo")) else if (command.equals("ShowInfo"))
{ {
@@ -317,269 +218,160 @@ public class RaceManagerInstance extends Npc
{ {
return; return;
} }
showMonsterInfo(player);
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
html.setFile(player, getHtmlPath(getId(), 6, player));
for (int i = 0; i < 8; i++)
{
int n = i + 1;
String search = "Mob" + n;
html.replace(search, MonsterRace.getInstance().getMonsters()[i].getTemplate().getName());
} }
else if (command.equals("calculateWin")) html.replace("%objectId%", getObjectId());
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
else if (command.equals("ShowTickets"))
{ {
if (!Config.ALLOW_RACE) if (!Config.ALLOW_RACE)
{ {
super.onBypassFeedback(player, "Chat 0");
return; return;
} }
// displayCalculateWinnings(player);
// Generate data.
final StringBuilder sb = new StringBuilder();
// Retrieve player's tickets.
for (ItemInstance ticket : player.getInventory().getAllItemsByItemId(4443))
{
// Don't list current race tickets.
if (ticket.getEnchantLevel() == MonsterRace.getInstance().getRaceNumber())
{
continue;
} }
else if (command.equals("viewHistory"))
StringUtil.append(sb, "<tr><td><a action=\"bypass -h npc_%objectId%_ShowTicket ", "" + ticket.getObjectId(), "\">", "" + ticket.getEnchantLevel(), " Race Number</a></td><td align=right><font color=\"LEVEL\">", "" + ticket.getCustomType1(), "</font> Number</td><td align=right><font color=\"LEVEL\">", "" + (ticket.getCustomType2() * 100), "</font> Adena</td></tr>");
}
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
html.setFile(player, getHtmlPath(getId(), 7, player));
html.replace("%tickets%", sb.toString());
html.replace("%objectId%", getObjectId());
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
else if (command.startsWith("ShowTicket"))
{
// Retrieve ticket objectId.
final int val = Integer.parseInt(command.substring(11));
if (!Config.ALLOW_RACE || (val == 0))
{
super.onBypassFeedback(player, "Chat 0");
return;
}
// Retrieve ticket on player's inventory.
final ItemInstance ticket = player.getInventory().getItemByObjectId(val);
if (ticket == null)
{
super.onBypassFeedback(player, "Chat 0");
return;
}
final int raceId = ticket.getEnchantLevel();
final int lane = ticket.getCustomType1();
final int bet = ticket.getCustomType2() * 100;
// Retrieve HistoryInfo for that race.
final HistoryInfo info = MonsterRace.getInstance().getHistory().get(raceId - 1);
if (info == null)
{
super.onBypassFeedback(player, "Chat 0");
return;
}
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
html.setFile(player, getHtmlPath(getId(), 8, player));
html.replace("%raceId%", raceId);
html.replace("%lane%", lane);
html.replace("%bet%", bet);
html.replace("%firstLane%", info.getFirst());
html.replace("%odd%", (lane == info.getFirst()) ? String.format(Locale.ENGLISH, "%.2f", info.getOddRate()) : "0.01");
html.replace("%objectId%", getObjectId());
html.replace("%ticketObjectId%", val);
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
else if (command.startsWith("CalculateWin"))
{
// Retrieve ticket objectId.
final int val = Integer.parseInt(command.substring(13));
if (!Config.ALLOW_RACE || (val == 0))
{
super.onBypassFeedback(player, "Chat 0");
return;
}
// Delete ticket on player's inventory.
final ItemInstance ticket = player.getInventory().getItemByObjectId(val);
if (ticket == null)
{
super.onBypassFeedback(player, "Chat 0");
return;
}
final int raceId = ticket.getEnchantLevel();
final int lane = ticket.getCustomType1();
final int bet = ticket.getCustomType2() * 100;
// Retrieve HistoryInfo for that race.
final HistoryInfo info = MonsterRace.getInstance().getHistory().get(raceId - 1);
if (info == null)
{
super.onBypassFeedback(player, "Chat 0");
return;
}
// Destroy the ticket.
if (player.destroyItem("MonsterTrack", ticket, this, true))
{
player.addAdena("MonsterTrack", (int) (bet * ((lane == info.getFirst()) ? info.getOddRate() : 0.01)), this, true);
}
super.onBypassFeedback(player, "Chat 0");
return;
}
else if (command.equals("ViewHistory"))
{ {
if (!Config.ALLOW_RACE) if (!Config.ALLOW_RACE)
{ {
super.onBypassFeedback(player, "Chat 0");
return; return;
} }
// displayHistory(player);
// Generate data.
final StringBuilder sb = new StringBuilder();
// Use whole history, pickup from 'last element' and stop at 'latest element - 7'.
final List<HistoryInfo> history = MonsterRace.getInstance().getHistory();
for (int i = history.size() - 1; i >= Math.max(0, history.size() - 7); i--)
{
final HistoryInfo info = history.get(i);
StringUtil.append(sb, "<tr><td><font color=\"LEVEL\">", "" + info.getRaceId(), "</font> th</td><td><font color=\"LEVEL\">", "" + info.getFirst(), "</font> Lane </td><td><font color=\"LEVEL\">", "" + info.getSecond(), "</font> Lane</td><td align=right><font color=00ffff>", String.format(Locale.ENGLISH, "%.2f", info.getOddRate()), "</font> Times</td></tr>");
}
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
html.setFile(player, getHtmlPath(getId(), 9, player));
html.replace("%infos%", sb.toString());
html.replace("%objectId%", getObjectId());
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
} }
else else
{ {
// getKnownList().removeKnownObject(player);
super.onBypassFeedback(player, command); super.onBypassFeedback(player, command);
} }
} }
public void showOdds(PlayerInstance player)
{
if (_state == ACCEPTING_BETS)
{
return;
}
final int npcId = getTemplate().getId();
String filename;
String search;
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
filename = getHtmlPath(npcId, 5, player);
html.setFile(player, filename);
for (int i = 0; i < 8; i++)
{
final int n = i + 1;
search = "Mob" + n;
html.replace(search, MonsterRace.getInstance().getMonsters()[i].getTemplate().getName());
}
html.replace("1race", String.valueOf(_raceNumber));
html.replace("%objectId%", String.valueOf(getObjectId()));
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
public void showMonsterInfo(PlayerInstance player)
{
final int npcId = getTemplate().getId();
String filename;
String search;
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
filename = getHtmlPath(npcId, 6, player);
html.setFile(player, filename);
for (int i = 0; i < 8; i++)
{
final int n = i + 1;
search = "Mob" + n;
html.replace(search, MonsterRace.getInstance().getMonsters()[i].getTemplate().getName());
}
html.replace("%objectId%", String.valueOf(getObjectId()));
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
public void showBuyTicket(PlayerInstance player, int value)
{
if (_state != ACCEPTING_BETS)
{
return;
}
final int npcId = getTemplate().getId();
SystemMessage sm;
String filename;
String search;
String replace;
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
if (value < 10)
{
filename = getHtmlPath(npcId, 2, player);
html.setFile(player, filename);
for (int i = 0; i < 8; i++)
{
final int n = i + 1;
search = "Mob" + n;
html.replace(search, MonsterRace.getInstance().getMonsters()[i].getTemplate().getName());
}
search = "No1";
if (value == 0)
{
html.replace(search, "");
}
else
{
html.replace(search, Integer.toString(value));
player.setRace(0, value);
}
}
else if (value < 20)
{
if (player.getRace(0) == 0)
{
return;
}
filename = getHtmlPath(npcId, 3, player);
html.setFile(player, filename);
html.replace("0place", Integer.toString(player.getRace(0)));
search = "Mob1";
replace = MonsterRace.getInstance().getMonsters()[player.getRace(0) - 1].getTemplate().getName();
html.replace(search, replace);
search = "0adena";
if (value == 10)
{
html.replace(search, "");
}
else
{
html.replace(search, Integer.toString(_cost[value - 11]));
player.setRace(1, value - 10);
}
}
else if (value == 20)
{
if ((player.getRace(0) == 0) || (player.getRace(1) == 0))
{
return;
}
filename = getHtmlPath(npcId, 4, player);
html.setFile(player, filename);
html.replace("0place", Integer.toString(player.getRace(0)));
search = "Mob1";
replace = MonsterRace.getInstance().getMonsters()[player.getRace(0) - 1].getTemplate().getName();
html.replace(search, replace);
search = "0adena";
final int price = _cost[player.getRace(1) - 1];
html.replace(search, Integer.toString(price));
search = "0tax";
final int tax = 0;
html.replace(search, Integer.toString(tax));
search = "0total";
final int total = price + tax;
html.replace(search, Integer.toString(total));
}
else
{
if ((player.getRace(0) == 0) || (player.getRace(1) == 0))
{
return;
}
final int ticket = player.getRace(0);
final int priceId = player.getRace(1);
if (!player.reduceAdena("Race", _cost[priceId - 1], this, true))
{
return;
}
player.setRace(0, 0);
player.setRace(1, 0);
sm = new SystemMessage(SystemMessageId.ACQUIRED_S1_S2);
sm.addInt(_raceNumber);
sm.addItemName(4443);
player.sendPacket(sm);
final ItemInstance item = new ItemInstance(IdFactory.getInstance().getNextId(), 4443);
item.setCount(1);
item.setEnchantLevel(_raceNumber);
item.setCustomType1(ticket);
item.setCustomType2(_cost[priceId - 1] / 100);
player.getInventory().addItem("Race", item, player, this);
final InventoryUpdate iu = new InventoryUpdate();
iu.addItem(item);
final ItemInstance adenaupdate = player.getInventory().getItemByItemId(Inventory.ADENA_ID);
iu.addModifiedItem(adenaupdate);
player.sendInventoryUpdate(iu);
return;
}
html.replace("1race", String.valueOf(_raceNumber));
html.replace("%objectId%", String.valueOf(getObjectId()));
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
public static class Race
{
private final Info[] _info;
public Race(Info[] pInfo)
{
_info = pInfo;
}
public Info getLaneInfo(int lane)
{
return _info[lane];
}
public class Info
{
private final int _id;
private final int _place;
private final int _odds;
private final int _payout;
public Info(int pId, int pPlace, int pOdds, int pPayout)
{
_id = pId;
_place = pPlace;
_odds = pOdds;
_payout = pPayout;
}
public int getId()
{
return _id;
}
public int getOdds()
{
return _odds;
}
public int getPayout()
{
return _payout;
}
public int getPlace()
{
return _place;
}
}
}
class RunRace implements Runnable
{
@Override
public void run()
{
_packet = new MonRaceInfo(_codes[2][0], _codes[2][1], MonsterRace.getInstance().getMonsters(), MonsterRace.getInstance().getSpeeds());
sendMonsterInfo();
ThreadPool.schedule(new RunEnd(), 30000);
}
}
class RunEnd implements Runnable
{
@Override
public void run()
{
makeAnnouncement(SystemMessageId.FIRST_PRIZE_GOES_TO_THE_PLAYER_IN_LANE_S1_SECOND_PRIZE_GOES_TO_THE_PLAYER_IN_LANE_S2);
makeAnnouncement(SystemMessageId.MONSTER_RACE_S1_IS_FINISHED);
_raceNumber++;
DeleteObject obj = null;
for (int i = 0; i < 8; i++)
{
obj = new DeleteObject(MonsterRace.getInstance().getMonsters()[i]);
broadcast(obj);
MonsterRace.getInstance().getMonsters()[i].deleteMe();
}
}
}
} }

View File

@@ -20,10 +20,12 @@ import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.l2jmobius.gameserver.enums.ChatType; import org.l2jmobius.gameserver.enums.ChatType;
import org.l2jmobius.gameserver.instancemanager.ZoneManager;
import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.World;
import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.actor.Creature;
import org.l2jmobius.gameserver.model.actor.Summon; import org.l2jmobius.gameserver.model.actor.Summon;
import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
import org.l2jmobius.gameserver.model.zone.ZoneType;
import org.l2jmobius.gameserver.network.serverpackets.CharInfo; import org.l2jmobius.gameserver.network.serverpackets.CharInfo;
import org.l2jmobius.gameserver.network.serverpackets.CreatureSay; import org.l2jmobius.gameserver.network.serverpackets.CreatureSay;
import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage; import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
@@ -191,4 +193,29 @@ public class Broadcast
{ {
toAllOnlinePlayers(new ExShowScreenMessage(text, 10000)); toAllOnlinePlayers(new ExShowScreenMessage(text, 10000));
} }
/**
* Send a packet to all players in a specific zone type.
* @param <T> ZoneType.
* @param zoneType : The zone type to send packets.
* @param packets : The packets to send.
*/
public static <T extends ZoneType> void toAllPlayersInZoneType(Class<T> zoneType, IClientOutgoingPacket... packets)
{
for (ZoneType zone : ZoneManager.getInstance().getAllZones(zoneType))
{
for (Creature creature : zone.getCharactersInside())
{
if (creature == null)
{
continue;
}
for (IClientOutgoingPacket packet : packets)
{
creature.sendPacket(packet);
}
}
}
}
} }

View File

@@ -0,0 +1,15 @@
CREATE TABLE IF NOT EXISTS `mdt_bets` (
`lane_id` INT(1) DEFAULT 0,
`bet` INT DEFAULT 0,
PRIMARY KEY (`lane_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;
INSERT INTO `mdt_bets` VALUES
('1','0'),
('2','0'),
('3','0'),
('4','0'),
('5','0'),
('6','0'),
('7','0'),
('8','0');

View File

@@ -0,0 +1,7 @@
CREATE TABLE IF NOT EXISTS `mdt_history` (
`race_id` MEDIUMINT DEFAULT 0,
`first` INT(1) DEFAULT 0,
`second` INT(1) DEFAULT 0,
`odd_rate` DOUBLE(10,2) DEFAULT 0,
PRIMARY KEY (`race_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;

View File

@@ -416,9 +416,6 @@
<admin command="admin_mobgroup_attackgrp" accessLevel="100" /> <admin command="admin_mobgroup_attackgrp" accessLevel="100" />
<admin command="admin_mobgroup_invul" accessLevel="100" /> <admin command="admin_mobgroup_invul" accessLevel="100" />
<!-- ADMIN MONSTER RACE -->
<admin command="admin_mons" accessLevel="100" />
<!-- ADMIN ONLINE --> <!-- ADMIN ONLINE -->
<admin command="admin_online" accessLevel="100" /> <admin command="admin_online" accessLevel="100" />

View File

@@ -1,19 +1,22 @@
<html><body><br><br> <html><body>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 0">Back</Button><br> <br>
In a monster race eight monsters race at the same time. If you guess which monster is expected to finish first and buy that monster's race ticket, based upon the odds to win you will receive an appropriate amount of Adena.<br> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 0">Go Back</Button>
<font color="LEVEL">How the Race Works</font>Monster races run every 20 minutes in real time.<br> <br>
<font color="LEVEL">How to Participate</font><br> In a monster race eight monsters race at the same time. If you guess which monster is expected to finish first and buy that monster's race ticket, based upon the odds to win you will receive an appropriate amount of adena.<br>
You can purchase a monster race ticket through a race manager NPC. Only between 18 minutes before the start of a race until 3 minutes before the race begins can you buy a ticket for the applicable race. When you purchase a ticket through a race manager, you can find out the names of the participating monsters and their current statistics. Three minutes before a race starts, the race manager will stop ticket sales and announce the monsters' individual odds for the current race.<br> <font color="LEVEL">Racing Schedule</font><br>
<font color="LEVEL">Betting</font><br> Monster races every 20 minutes in real time.<br>
There are two types of race betting. The Win bet means that you back a particular monster to win 1st place. The Place bet means that you will have to guess the 1st and 2nd place monsters, regardless of who finishes first or second.<br> <font color="LEVEL">Method of Race Participation</font><br>
You can purchase a monster race ticket through a race manager NPC. Only between 18 minutes before the start of a race until 3 minutes before the race begins you can buy a ticket for the applicable race. When you purchase a ticket through a conversation with a race manager, you can find out the names of participating monsters in the race and their current statistics. Five minutes before a race starts, the race manager will stop ticket sales and announce monsters' individual odds rate for the current race.<br>
<font color="LEVEL">Types of Race</font><br>
There are two types of race betting. The Win type betting means that you back a monster that will win the first prize. Place type of betting means that you will have to guess the 1st and 2nd winning monsters, regardless of who finishes first or second.<br>
<font color="00FFFF">Win</font><br1> <font color="00FFFF">Win</font><br1>
* Among the entire betting amount for Win betting, after subtracting the race track's own share of profit, the amount left will be your total winnings. Three minutes before the race, each monster's expected odds are announced. Each Win ticket's dividend amount is calculated by multiplying the odds by the price of a ticket. The odds are never less than 1.<br1> * Among the entire betting amount for Win betting, after subtracting the race track's own share of profit, the amount left will be your total winnings. Three minutes before the race each monster's expected odds are announced. Each Win ticket's dividend amount is calculated by multiplying the odds by the price of a ticket. The odds rate is never less than 1.<br1>
* The prize money does not get transferred to the next race.<br> * The prize money does not get transferred to the next race.<br>
<font color="00FFFF">Place</font><br1> <font color="00FFFF">Place</font><br1>
* Place betting is a way of betting when you correctly guess the two monsters who will finish first and second regardless of their order. The method of calculating the dividend amount is the same as Win betting.<br1> * Place betting is a way of betting when you correctly guess the two monsters who will finish first and second regardless of their order. The method of calculating dividend amount is the same as Win betting.<br>
* Currently place betting is not available.<br> * Currently place betting is not offered.<br>
<font color="LEVEL">End of Race</font><br> <font color="LEVEL">End of Race</font><br>
When a race is over, the race manager will shout the results of the race. You can trade your ticket for Adena by speaking to a race manager NPC. Check the Winning Information Data to find out the types of bets and total winnings for the race you bet on, as well as your win status and prize amount. If you won, you can receive your prize money thorough a race manager NPC. If you did not win, a race manager will buy back your ticket for a small amount of money.<br> When a race is over, the race manager will shout out the results of the race. Through a conversation with a race manager NPC, you can trade your ticket for adena. By looking up on the Winning Information Data, you can find out the types of tickets and quantity of the winnings in the race you betted on and your win status and prize amount you won. If you won, you can receive your prize money thorough a race manager NPC. If you did not win, a race manager will buy back your ticket by paying you a very small amount of money.<br>
If a server goes down while tickets are being sold, or while a race is in progress, the race will be cancelled. You can receive a full refund for your ticket for a cancelled race through a race manager.<br> If a server goes down while tickets are being sold, or while a race is in progress, the race will be cancelled. You can receive a full refund for your ticket for a cancelled race through a race manager.<br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 0">Back</Button> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 0">Go Back</Button>
</body></html> </body></html>

View File

@@ -3,6 +3,7 @@
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=1 height=3> <img src="L2UI.SquareBlank" width=1 height=3>
</center> </center>
<table width=270 border=0 bgcolor="111111"> <table width=270 border=0 bgcolor="111111">
<tr> <tr>
<td fixwidth=60>Lane Selection</td> <td fixwidth=60>Lane Selection</td>
@@ -11,6 +12,7 @@
<td fixwidth=30 align=right>Odds</td> <td fixwidth=30 align=right>Odds</td>
</tr> </tr>
</table> </table>
<table width=270 border=0> <table width=270 border=0>
<tr> <tr>
<td fixwidth=13><img src="L2UI.rightBtn2" width=14 height=13></td> <td fixwidth=13><img src="L2UI.rightBtn2" width=14 height=13></td>
@@ -20,18 +22,21 @@
<td fixwidth=30 align=right><font color="00ffff">13%</font></td> <td fixwidth=30 align=right><font color="00ffff">13%</font></td>
</tr> </tr>
</table> </table>
<table width=270 border=0> <table width=270 border=0>
<tr><td width=100>Select Bet Amount</td> <tr><td width=100>Select Bet Amount</td>
<td width=170> <td width=170>
<a action="bypass -h npc_%objectId%_BuyTicket 11" >100 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 12" >500 Adena</a><br1> <a action="bypass -h npc_%objectId%_BuyTicket 11" >100 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 12" >500 Adena</a> <br1>
<a action="bypass -h npc_%objectId%_BuyTicket 13" >1,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 14" >5,000 Adena</a><br1> <a action="bypass -h npc_%objectId%_BuyTicket 13" >1,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 14" >5,000 Adena</a> <br1>
<a action="bypass -h npc_%objectId%_BuyTicket 15" >10,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 16" >20,000 Adena</a><br1> <a action="bypass -h npc_%objectId%_BuyTicket 15" >10,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 16" >20,000 Adena</a> <br1>
<a action="bypass -h npc_%objectId%_BuyTicket 17" >50,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 18" >100,000 Adena</a><br1> <a action="bypass -h npc_%objectId%_BuyTicket 17" >50,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 18" >100,000 Adena</a> <br1>
</td></tr> </td></tr>
</table> </table>
<img src="L2UI.SquareBlank" width=270 height=10> <img src="L2UI.SquareBlank" width=270 height=10>
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=270 height=5> <img src="L2UI.SquareBlank" width=270 height=5>
<table width=270 border=0 cellpadding=0 cellspacing=0> <table width=270 border=0 cellpadding=0 cellspacing=0>
<tr> <tr>
<td width=70 height=20><button value="Select Lane" action="bypass -h npc_%objectId%_BuyTicket 0" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td> <td width=70 height=20><button value="Select Lane" action="bypass -h npc_%objectId%_BuyTicket 0" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td>
@@ -39,4 +44,5 @@
<td width=70 align=right><button value="Confirm" action="bypass -h npc_%objectId%_BuyTicket 20" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td> <td width=70 align=right><button value="Confirm" action="bypass -h npc_%objectId%_BuyTicket 20" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td>
</tr> </tr>
</table> </table>
</body></html> </body></html>

View File

@@ -11,6 +11,7 @@
<td fixwidth=30 align=right>Odds</td> <td fixwidth=30 align=right>Odds</td>
</tr> </tr>
</table> </table>
<table width=270 border=0> <table width=270 border=0>
<tr> <tr>
<td fixwidth=13><img src="L2UI.rightBtn2" width=14 height=13></td> <td fixwidth=13><img src="L2UI.rightBtn2" width=14 height=13></td>
@@ -20,6 +21,7 @@
<td fixwidth=30 align=right><font color="00ffff">13%</font></td> <td fixwidth=30 align=right><font color="00ffff">13%</font></td>
</tr> </tr>
</table> </table>
<table width=270 border=0 bgcolor=111111> <table width=270 border=0 bgcolor=111111>
<tr> <tr>
<td fixwidth=60>Bet Amount </td> <td fixwidth=60>Bet Amount </td>
@@ -37,11 +39,13 @@
<td>Total </td> <td>Total </td>
<td><font color="LEVEL">0total</font> Adena</td> <td><font color="LEVEL">0total</font> Adena</td>
</tr> </tr>
</table><br> </table>
<br>
<center>If the information is correct, please click Confirm.</center><br> <center>If the information is correct, please click Confirm.</center><br>
<img src="L2UI.SquareBlank" width=270 height=10> <img src="L2UI.SquareBlank" width=270 height=10>
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=270 height=5> <img src="L2UI.SquareBlank" width=270 height=5>
<table width=270 border=0 cellpadding=0 cellspacing=0> <table width=270 border=0 cellpadding=0 cellspacing=0>
<tr> <tr>
<td width=70 height=20><button value=&$141; action="bypass -h npc_%objectId%_Chat 0" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td> <td width=70 height=20><button value=&$141; action="bypass -h npc_%objectId%_Chat 0" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td>

View File

@@ -15,49 +15,49 @@
<tr> <tr>
<td fixwidth=40>No.1</td> <td fixwidth=40>No.1</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob1</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob1</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd1</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.2</td> <td fixwidth=40>No.2</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob2</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob2</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd2</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.3</td> <td fixwidth=40>No.3</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob3</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob3</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd3</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.4</td> <td fixwidth=40>No.4</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob4</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob4</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd4</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.5</td> <td fixwidth=40>No.5</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob5</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob5</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd5</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.6</td> <td fixwidth=40>No.6</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob6</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob6</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd6</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.7</td> <td fixwidth=40>No.7</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob7</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob7</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd7</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.8</td> <td fixwidth=40>No.8</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob8</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob8</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd8</font> </td>
</tr> </tr>
</table> </table>
<img src="L2UI.SquareBlank" width=270 height=10> <img src="L2UI.SquareBlank" width=270 height=10>

View File

@@ -3,22 +3,12 @@
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=1 height=3> <img src="L2UI.SquareBlank" width=1 height=3>
<table width=270 border=0 bgcolor=111111> <table width=270 border=0 bgcolor=111111>
<tr><td fixwidth=60>Winnings for a Race.</td><td fixwidth=60 align=right>Lane</td><td fixwidth=160 align=right>Bet Amount</td></tr> <tr><td fixwidth=70>Winnings for a Race.</td><td fixwidth=70 align=right>Lane</td><td fixwidth=130 align=right>Bet Amount</td></tr>
<tr><td fixwidth=60 fixheight=1></td><td fixwidth=60></td><td fixwidth=160></td></tr> <tr><td fixwidth=70 fixheight=1></td><td fixwidth=70></td><td fixwidth=130></td></tr>
</table> </table>
<table width=270 border=0> <table width=270 border=0>
<tr> %tickets%
<td><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 8">1 Race Number</Button></td>
<td align=right><font color="LEVEL">1</font> Number</td>
<td align=right><font color="LEVEL">100</font> Adena</td>
</tr>
</table> </table>
<img src="L2UI.SquareWhite" width=270 height=1>
<table width=270 border=0>
<tr><td width=90><Button ALIGN=LEFT ICON="NORMAL" action="bypass _mrsl01_1">Previous List</Button></td><td width=90 align=center>1 Page </td><td width=90 align=right><Button ALIGN=LEFT ICON="NORMAL" action="bypass _mrsl01_2"> Next List</Button></td></tr>
</table>
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=270 height=5> <img src="L2UI.SquareBlank" width=270 height=5>
<table width=270 border=0 cellpadding=0 cellspacing=0> <table width=270 border=0 cellpadding=0 cellspacing=0>

View File

@@ -4,11 +4,11 @@
<img src="L2UI.SquareBlank" width=1 height=3> <img src="L2UI.SquareBlank" width=1 height=3>
<table width=270 border=0> <table width=270 border=0>
<tr> <tr>
<td width=230>Race #19090<br> <td width=230>Race #%raceId%<br>
Lane 1<br> Lane: %lane%<br>
Bet: 100 adena<br> Bet: %bet% Adena<br>
1st place: Lane [4]<br> 1st place: Lane [%firstLane%]<br>
Odds:[0.01] Odds: [%odd%]
</td> </td>
</tr> </tr>
</table> </table>
@@ -18,8 +18,8 @@ Odds:[0.01]
<table width=270 border=0 cellpadding=0 cellspacing=0> <table width=270 border=0 cellpadding=0 cellspacing=0>
<tr> <tr>
<td width=90 height=20><button value="Previous" action="bypass -h npc_%objectId%_Chat 7" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td> <td width=90 height=20><button value="Previous" action="bypass -h npc_%objectId%_ShowTickets" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td>
<td width=90></td> <td width=90></td>
<td width=90 align=right><button value="Calculate" action="bypass _mrsl03_30635184" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td> <td width=90 align=right><button value="Calculate" action="bypass -h npc_%objectId%_CalculateWin %ticketObjectId%" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td>
</tr></table> </tr></table>
</body></html> </body></html>

View File

@@ -7,41 +7,7 @@
</table> </table>
<table width=270 border=0> <table width=270 border=0>
<tr><td fixwidth=60 fixheight=1></td><td fixwidth=70></td><td fixwidth=70></td><td fixwidth=70></td></tr> <tr><td fixwidth=60 fixheight=1></td><td fixwidth=70></td><td fixwidth=70></td><td fixwidth=70></td></tr>
<tr> %infos%
<td><font color="LEVEL">7</font> th</td>
<td><font color="LEVEL">8</font> Lane </td><td><font color="LEVEL">1</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">6</font> th</td>
<td><font color="LEVEL">4</font> Lane </td><td><font color="LEVEL">8</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">5</font> th</td>
<td><font color="LEVEL">7</font> Lane </td><td><font color="LEVEL">2</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">4</font> th</td>
<td><font color="LEVEL">6</font> Lane </td><td><font color="LEVEL">8</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">3</font> th</td>
<td><font color="LEVEL">4</font> Lane </td><td><font color="LEVEL">1</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">2</font> th</td>
<td><font color="LEVEL">1</font> Lane </td><td><font color="LEVEL">8</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">1</font> th</td>
<td><font color="LEVEL">7</font> Lane </td><td><font color="LEVEL">6</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
</table> </table>
<img src="L2UI.SquareBlank" width=270 height=10> <img src="L2UI.SquareBlank" width=270 height=10>
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>

View File

@@ -1,15 +1,15 @@
<html>
<body>
<center>Monster Race Manager</center> <center>Monster Race Manager</center>
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareBlank" width=1 height=3> <img src="L2UI.SquareWhite" width=270 height=1>
<table width=270 border=0> <img src="L2UI.SquareBlank" width=1 height=3>
<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 1">Monster Race Help</Button></td></tr> <br>
<!--<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_ShowOdds">View Odds</Button></td></tr>--> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 1">Monster Race Help</Button>
<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_ShowInfo">View Monster Information</Button></td></tr> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_ShowOdds">View Odds</Button>
<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_BuyTicket 0">Purchase Ticket</Button></td></tr> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_ShowInfo">View Monster Information</Button>
<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 7">Calculate Winnings</Button></td></tr> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_BuyTicket 0">Purchase Ticket</Button>
<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 9">View Past Results</Button></td></tr> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_ShowTickets">Calculate Winnings</Button>
<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Quest TeleportToRaceTrack" msg="1298;Monster Race Track">Exit the Monster Race Track</Button></td></tr> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_ViewHistory">View Past Results</Button>
</table> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Quest TeleportToRaceTrack" msg="1298;Monster Race Track">Exit the monster race track.</Button>
<img src="L2UI.SquareBlank" width=270 height=10> <img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareBlank" width=270 height=5> <img src="L2UI.SquareBlank" width=270 height=10>
<img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=270 height=5>
</body></html> </body></html>

View File

@@ -105,7 +105,6 @@ import handlers.admincommandhandlers.AdminMenu;
import handlers.admincommandhandlers.AdminMessages; import handlers.admincommandhandlers.AdminMessages;
import handlers.admincommandhandlers.AdminMissingHtmls; import handlers.admincommandhandlers.AdminMissingHtmls;
import handlers.admincommandhandlers.AdminMobGroup; import handlers.admincommandhandlers.AdminMobGroup;
import handlers.admincommandhandlers.AdminMonsterRace;
import handlers.admincommandhandlers.AdminOlympiad; import handlers.admincommandhandlers.AdminOlympiad;
import handlers.admincommandhandlers.AdminOnline; import handlers.admincommandhandlers.AdminOnline;
import handlers.admincommandhandlers.AdminPForge; import handlers.admincommandhandlers.AdminPForge;
@@ -442,7 +441,6 @@ public class MasterHandler
AdminMessages.class, AdminMessages.class,
AdminMissingHtmls.class, AdminMissingHtmls.class,
AdminMobGroup.class, AdminMobGroup.class,
AdminMonsterRace.class,
AdminOlympiad.class, AdminOlympiad.class,
AdminOnline.class, AdminOnline.class,
AdminPathNode.class, AdminPathNode.class,

View File

@@ -1,166 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package handlers.admincommandhandlers;
import org.l2jmobius.commons.concurrent.ThreadPool;
import org.l2jmobius.gameserver.MonsterRace;
import org.l2jmobius.gameserver.handler.IAdminCommandHandler;
import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
import org.l2jmobius.gameserver.network.SystemMessageId;
import org.l2jmobius.gameserver.network.serverpackets.DeleteObject;
import org.l2jmobius.gameserver.network.serverpackets.MonRaceInfo;
import org.l2jmobius.gameserver.network.serverpackets.PlaySound;
import org.l2jmobius.gameserver.network.serverpackets.SystemMessage;
/**
* This class handles following admin commands: - invul = turns invulnerability on/off
* @version $Revision: 1.1.6.4 $ $Date: 2007/07/31 10:06:00 $
*/
public class AdminMonsterRace implements IAdminCommandHandler
{
private static final String[] ADMIN_COMMANDS =
{
"admin_mons"
};
static int state = -1;
@Override
public boolean useAdminCommand(String command, PlayerInstance activeChar)
{
if (command.equalsIgnoreCase("admin_mons"))
{
handleSendPacket(activeChar);
}
return true;
}
@Override
public String[] getAdminCommandList()
{
return ADMIN_COMMANDS;
}
private void handleSendPacket(PlayerInstance activeChar)
{
/*
* -1 0 to initialize the race 0 15322 to start race 13765 -1 in middle of race -1 0 to end the race 8003 to 8027
*/
final int[][] codes =
{
{
-1,
0
},
{
0,
15322
},
{
13765,
-1
},
{
-1,
0
}
};
final MonsterRace race = MonsterRace.getInstance();
if (state == -1)
{
state++;
race.newRace();
race.newSpeeds();
final MonRaceInfo spk = new MonRaceInfo(codes[state][0], codes[state][1], race.getMonsters(), race.getSpeeds());
activeChar.sendPacket(spk);
activeChar.broadcastPacket(spk);
}
else if (state == 0)
{
state++;
final SystemMessage sm = new SystemMessage(SystemMessageId.THEY_RE_OFF);
sm.addInt(0);
activeChar.sendPacket(sm);
final PlaySound sRace = new PlaySound(1, "S_Race", 0, 0, 0, 0, 0);
activeChar.sendPacket(sRace);
activeChar.broadcastPacket(sRace);
final PlaySound sRace2 = new PlaySound(0, "ItemSound2.race_start", 1, 121209259, 12125, 182487, -3559);
activeChar.sendPacket(sRace2);
activeChar.broadcastPacket(sRace2);
final MonRaceInfo spk = new MonRaceInfo(codes[state][0], codes[state][1], race.getMonsters(), race.getSpeeds());
activeChar.sendPacket(spk);
activeChar.broadcastPacket(spk);
ThreadPool.schedule(new RunRace(codes, activeChar), 5000);
}
}
private class RunRace implements Runnable
{
private final int[][] codes;
private final PlayerInstance activeChar;
public RunRace(int[][] pCodes, PlayerInstance pActiveChar)
{
codes = pCodes;
activeChar = pActiveChar;
}
@Override
public void run()
{
// int[][] speeds1 = MonsterRace.getInstance().getSpeeds();
// MonsterRace.getInstance().newSpeeds();
// int[][] speeds2 = MonsterRace.getInstance().getSpeeds();
/*
* int[] speed = new int[8]; for (int i=0; i<8; i++) { for (int j=0; j<20; j++) { //LOGGER.info("Adding "+speeds1[i][j] +" and "+ speeds2[i][j]); speed[i] += (speeds1[i][j]*1); // + (speeds2[i][j]*1); } LOGGER.info("Total speed for "+(i+1)+" = "+speed[i]); }
*/
final MonRaceInfo spk = new MonRaceInfo(codes[2][0], codes[2][1], MonsterRace.getInstance().getMonsters(), MonsterRace.getInstance().getSpeeds());
activeChar.sendPacket(spk);
activeChar.broadcastPacket(spk);
ThreadPool.schedule(new RunEnd(activeChar), 30000);
}
}
private static class RunEnd implements Runnable
{
private final PlayerInstance activeChar;
public RunEnd(PlayerInstance pActiveChar)
{
activeChar = pActiveChar;
}
@Override
public void run()
{
DeleteObject obj = null;
for (int i = 0; i < 8; i++)
{
obj = new DeleteObject(MonsterRace.getInstance().getMonsters()[i]);
activeChar.sendPacket(obj);
activeChar.broadcastPacket(obj);
}
state = -1;
}
}
}

View File

@@ -144,6 +144,7 @@ import org.l2jmobius.gameserver.instancemanager.SiegeGuardManager;
import org.l2jmobius.gameserver.instancemanager.SiegeManager; import org.l2jmobius.gameserver.instancemanager.SiegeManager;
import org.l2jmobius.gameserver.instancemanager.WalkingManager; import org.l2jmobius.gameserver.instancemanager.WalkingManager;
import org.l2jmobius.gameserver.instancemanager.ZoneManager; import org.l2jmobius.gameserver.instancemanager.ZoneManager;
import org.l2jmobius.gameserver.instancemanager.games.MonsterRace;
import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.World;
import org.l2jmobius.gameserver.model.entity.Hero; import org.l2jmobius.gameserver.model.entity.Hero;
import org.l2jmobius.gameserver.model.events.EventDispatcher; import org.l2jmobius.gameserver.model.events.EventDispatcher;
@@ -404,10 +405,8 @@ public class GameServer
ItemsAutoDestroy.getInstance(); ItemsAutoDestroy.getInstance();
} }
if (Config.ALLOW_RACE)
{
MonsterRace.getInstance(); MonsterRace.getInstance();
}
TaskManager.getInstance(); TaskManager.getInstance();
AntiFeedManager.getInstance().registerEvent(AntiFeedManager.GAME_ID); AntiFeedManager.getInstance().registerEvent(AntiFeedManager.GAME_ID);

View File

@@ -1,140 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.data.xml.impl.NpcData;
import org.l2jmobius.gameserver.model.actor.Npc;
import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate;
public class MonsterRace
{
protected static final Logger LOGGER = Logger.getLogger(MonsterRace.class.getName());
private final Npc[] _monsters;
private int[][] _speeds;
private final int[] _first;
private final int[] _second;
protected MonsterRace()
{
_monsters = new Npc[8];
_speeds = new int[8][20];
_first = new int[2];
_second = new int[2];
}
public void newRace()
{
int random = 0;
for (int i = 0; i < 8; i++)
{
final int id = 31003;
random = Rnd.get(24);
while (true)
{
for (int j = i - 1; j >= 0; j--)
{
if (_monsters[j].getTemplate().getId() == (id + random))
{
random = Rnd.get(24);
}
}
break;
}
try
{
final NpcTemplate template = NpcData.getInstance().getTemplate(id + random);
_monsters[i] = (Npc) Class.forName("org.l2jmobius.gameserver.model.actor.instance." + template.getType() + "Instance").getConstructors()[0].newInstance(template);
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Unable to create monster!", e);
}
}
newSpeeds();
}
public void newSpeeds()
{
_speeds = new int[8][20];
int total = 0;
_first[1] = 0;
_second[1] = 0;
for (int i = 0; i < 8; i++)
{
total = 0;
for (int j = 0; j < 20; j++)
{
_speeds[i][j] = j == 19 ? 100 : Rnd.get(60) + 65;
total += _speeds[i][j];
}
if (total >= _first[1])
{
_second[0] = _first[0];
_second[1] = _first[1];
_first[0] = 8 - i;
_first[1] = total;
}
else if (total >= _second[1])
{
_second[0] = 8 - i;
_second[1] = total;
}
}
}
/**
* @return Returns the monsters.
*/
public Npc[] getMonsters()
{
return _monsters;
}
/**
* @return Returns the speeds.
*/
public int[][] getSpeeds()
{
return _speeds;
}
public int getFirstPlace()
{
return _first[0];
}
public int getSecondPlace()
{
return _second[0];
}
public static MonsterRace getInstance()
{
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder
{
protected static final MonsterRace INSTANCE = new MonsterRace();
}
}

View File

@@ -0,0 +1,623 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.instancemanager.games;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.l2jmobius.Config;
import org.l2jmobius.commons.concurrent.ThreadPool;
import org.l2jmobius.commons.database.DatabaseFactory;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.data.xml.impl.NpcData;
import org.l2jmobius.gameserver.model.actor.Npc;
import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate;
import org.l2jmobius.gameserver.model.zone.type.DerbyTrackZone;
import org.l2jmobius.gameserver.network.SystemMessageId;
import org.l2jmobius.gameserver.network.serverpackets.DeleteObject;
import org.l2jmobius.gameserver.network.serverpackets.MonRaceInfo;
import org.l2jmobius.gameserver.network.serverpackets.PlaySound;
import org.l2jmobius.gameserver.network.serverpackets.SystemMessage;
import org.l2jmobius.gameserver.util.Broadcast;
public class MonsterRace
{
protected static final Logger LOGGER = Logger.getLogger(MonsterRace.class.getName());
public static enum RaceState
{
ACCEPTING_BETS,
WAITING,
STARTING_RACE,
RACE_END
}
protected static final PlaySound SOUND_1 = new PlaySound(1, "S_Race", 0, 0, 0, 0, 0);
protected static final PlaySound SOUND_2 = new PlaySound("ItemSound2.race_start");
protected static final int[][] CODES =
{
{
-1,
0
},
{
0,
15322
},
{
13765,
-1
}
};
protected final List<Integer> _npcTemplates = new ArrayList<>(); // List holding npc templates, shuffled on a new race.
protected final List<HistoryInfo> _history = new ArrayList<>(); // List holding old race records.
protected final Map<Integer, Long> _betsPerLane = new ConcurrentHashMap<>(); // Map holding all bets for each lane ; values setted to 0 after every race.
protected final List<Double> _odds = new ArrayList<>(); // List holding sorted odds per lane ; cleared at new odds calculation.
protected int _raceNumber = 1;
protected int _finalCountdown = 0;
protected RaceState _state = RaceState.RACE_END;
protected MonRaceInfo _packet;
private final Npc[] _monsters = new Npc[8];
private int[][] _speeds = new int[8][20];
private final int[] _first = new int[2];
private final int[] _second = new int[2];
protected MonsterRace()
{
if (!Config.ALLOW_RACE)
{
return;
}
// Feed _history with previous race results.
loadHistory();
// Feed _betsPerLane with stored informations on bets.
loadBets();
// Feed _npcTemplates, we will only have to shuffle it when needed.
for (int i = 31003; i < 31027; i++)
{
_npcTemplates.add(i);
}
ThreadPool.scheduleAtFixedRate(new Announcement(), 0, 1000);
}
public static class HistoryInfo
{
private final int _raceId;
private int _first;
private int _second;
private double _oddRate;
public HistoryInfo(int raceId, int first, int second, double oddRate)
{
_raceId = raceId;
_first = first;
_second = second;
_oddRate = oddRate;
}
public int getRaceId()
{
return _raceId;
}
public int getFirst()
{
return _first;
}
public int getSecond()
{
return _second;
}
public double getOddRate()
{
return _oddRate;
}
public void setFirst(int first)
{
_first = first;
}
public void setSecond(int second)
{
_second = second;
}
public void setOddRate(double oddRate)
{
_oddRate = oddRate;
}
}
private class Announcement implements Runnable
{
public Announcement()
{
}
@Override
public void run()
{
if (_finalCountdown > 1200)
{
_finalCountdown = 0;
}
switch (_finalCountdown)
{
case 0:
{
newRace();
newSpeeds();
_state = RaceState.ACCEPTING_BETS;
_packet = new MonRaceInfo(CODES[0][0], CODES[0][1], getMonsters(), getSpeeds());
final SystemMessage msg = new SystemMessage(SystemMessageId.TICKETS_ARE_NOW_AVAILABLE_FOR_MONSTER_RACE_S1);
msg.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, _packet, msg);
break;
}
case 30: // 30 sec
case 60: // 1 min
case 90: // 1 min 30 sec
case 120: // 2 min
case 150: // 2 min 30
case 180: // 3 min
case 210: // 3 min 30
case 240: // 4 min
case 270: // 4 min 30 sec
case 330: // 5 min 30 sec
case 360: // 6 min
case 390: // 6 min 30 sec
case 420: // 7 min
case 450: // 7 min 30
case 480: // 8 min
case 510: // 8 min 30
case 540: // 9 min
case 570: // 9 min 30 sec
case 630: // 10 min 30 sec
case 660: // 11 min
case 690: // 11 min 30 sec
case 720: // 12 min
case 750: // 12 min 30
case 780: // 13 min
case 810: // 13 min 30
case 870: // 14 min 30 sec
{
final SystemMessage msg = new SystemMessage(SystemMessageId.TICKETS_ARE_NOW_AVAILABLE_FOR_MONSTER_RACE_S1);
msg.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg);
break;
}
case 300: // 5 min
{
final SystemMessage msg = new SystemMessage(SystemMessageId.NOW_SELLING_TICKETS_FOR_MONSTER_RACE_S1);
msg.addInt(_raceNumber);
final SystemMessage msg2 = new SystemMessage(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S);
msg2.addInt(10);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg, msg2);
break;
}
case 600: // 10 min
{
final SystemMessage msg = new SystemMessage(SystemMessageId.NOW_SELLING_TICKETS_FOR_MONSTER_RACE_S1);
msg.addInt(_raceNumber);
final SystemMessage msg2 = new SystemMessage(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S);
msg2.addInt(5);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg, msg2);
break;
}
case 840: // 14 min
{
final SystemMessage msg = new SystemMessage(SystemMessageId.NOW_SELLING_TICKETS_FOR_MONSTER_RACE_S1);
msg.addInt(_raceNumber);
final SystemMessage msg2 = new SystemMessage(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S);
msg2.addInt(1);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg, msg2);
break;
}
case 900: // 15 min
{
_state = RaceState.WAITING;
calculateOdds();
final SystemMessage msg = new SystemMessage(SystemMessageId.TICKETS_ARE_NOW_AVAILABLE_FOR_MONSTER_RACE_S1);
msg.addInt(_raceNumber);
final SystemMessage msg2 = new SystemMessage(SystemMessageId.TICKETS_SALES_ARE_CLOSED_FOR_MONSTER_RACE_S1_ODDS_ARE_POSTED);
msg2.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg, msg2);
break;
}
case 960: // 16 min
case 1020: // 17 min
{
final int minutes = (_finalCountdown == 960) ? 2 : 1;
final SystemMessage msg = new SystemMessage(SystemMessageId.MONSTER_RACE_S2_WILL_BEGIN_IN_S1_MINUTE_S);
msg.addInt(minutes);
msg.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg);
break;
}
case 1050: // 17 min 30 sec
{
final SystemMessage msg = new SystemMessage(SystemMessageId.MONSTER_RACE_S1_WILL_BEGIN_IN_30_SECONDS);
msg.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg);
break;
}
case 1070: // 17 min 50 sec
{
final SystemMessage msg = new SystemMessage(SystemMessageId.MONSTER_RACE_S1_IS_ABOUT_TO_BEGIN_COUNTDOWN_IN_FIVE_SECONDS);
msg.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg);
break;
}
case 1075: // 17 min 55 sec
case 1076: // 17 min 56 sec
case 1077: // 17 min 57 sec
case 1078: // 17 min 58 sec
case 1079: // 17 min 59 sec
{
final int seconds = 1080 - _finalCountdown;
final SystemMessage msg = new SystemMessage(SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SECOND_S);
msg.addInt(seconds);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg);
break;
}
case 1080: // 18 min
{
_state = RaceState.STARTING_RACE;
_packet = new MonRaceInfo(CODES[1][0], CODES[1][1], getMonsters(), getSpeeds());
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, new SystemMessage(SystemMessageId.THEY_RE_OFF), SOUND_1, SOUND_2, _packet);
break;
}
case 1085: // 18 min 5 sec
{
_packet = new MonRaceInfo(CODES[2][0], CODES[2][1], getMonsters(), getSpeeds());
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, _packet);
break;
}
case 1115: // 18 min 35 sec
{
_state = RaceState.RACE_END;
// Populate history info with data, stores it in database.
final HistoryInfo info = _history.get(_history.size() - 1);
info.setFirst(getFirstPlace());
info.setSecond(getSecondPlace());
info.setOddRate(_odds.get(getFirstPlace() - 1));
saveHistory(info);
clearBets();
final SystemMessage msg = new SystemMessage(SystemMessageId.FIRST_PRIZE_GOES_TO_THE_PLAYER_IN_LANE_S1_SECOND_PRIZE_GOES_TO_THE_PLAYER_IN_LANE_S2);
msg.addInt(getFirstPlace());
msg.addInt(getSecondPlace());
final SystemMessage msg2 = new SystemMessage(SystemMessageId.MONSTER_RACE_S1_IS_FINISHED);
msg2.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg, msg2);
_raceNumber++;
break;
}
case 1140: // 19 min
{
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, new DeleteObject(getMonsters()[0]), new DeleteObject(getMonsters()[1]), new DeleteObject(getMonsters()[2]), new DeleteObject(getMonsters()[3]), new DeleteObject(getMonsters()[4]), new DeleteObject(getMonsters()[5]), new DeleteObject(getMonsters()[6]), new DeleteObject(getMonsters()[7]));
break;
}
}
_finalCountdown += 1;
}
}
public void newRace()
{
// Edit _history.
_history.add(new HistoryInfo(_raceNumber, 0, 0, 0));
// Randomize _npcTemplates.
Collections.shuffle(_npcTemplates);
// Setup 8 new creatures ; pickup the first 8 from _npcTemplates.
for (int i = 0; i < 8; i++)
{
try
{
final NpcTemplate template = NpcData.getInstance().getTemplate(_npcTemplates.get(i));
_monsters[i] = (Npc) Class.forName("org.l2jmobius.gameserver.model.actor.instance." + template.getType() + "Instance").getConstructors()[0].newInstance(template);
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "", e);
}
}
}
public void newSpeeds()
{
_speeds = new int[8][20];
int total = 0;
_first[1] = 0;
_second[1] = 0;
for (int i = 0; i < 8; i++)
{
total = 0;
for (int j = 0; j < 20; j++)
{
if (j == 19)
{
_speeds[i][j] = 100;
}
else
{
_speeds[i][j] = Rnd.get(60) + 65;
}
total += _speeds[i][j];
}
if (total >= _first[1])
{
_second[0] = _first[0];
_second[1] = _first[1];
_first[0] = 8 - i;
_first[1] = total;
}
else if (total >= _second[1])
{
_second[0] = 8 - i;
_second[1] = total;
}
}
}
/**
* Load past races informations, feeding _history arrayList.<br>
* Also sets _raceNumber, based on latest HistoryInfo loaded.
*/
protected void loadHistory()
{
try (Connection con = DatabaseFactory.getConnection())
{
PreparedStatement statement = con.prepareStatement("SELECT * FROM mdt_history");
ResultSet rset = statement.executeQuery();
while (rset.next())
{
_history.add(new HistoryInfo(rset.getInt("race_id"), rset.getInt("first"), rset.getInt("second"), rset.getDouble("odd_rate")));
_raceNumber++;
}
rset.close();
statement.close();
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, "MonsterRace: Can't load history: " + e.getMessage(), e);
}
LOGGER.info("MonsterRace: loaded " + _history.size() + " records, currently on race #" + _raceNumber);
}
/**
* Save an history record into database.
* @param history The infos to store.
*/
protected void saveHistory(HistoryInfo history)
{
try (Connection con = DatabaseFactory.getConnection())
{
PreparedStatement statement = con.prepareStatement("INSERT INTO mdt_history (race_id, first, second, odd_rate) VALUES (?,?,?,?)");
statement.setInt(1, history.getRaceId());
statement.setInt(2, history.getFirst());
statement.setInt(3, history.getSecond());
statement.setDouble(4, history.getOddRate());
statement.execute();
statement.close();
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, "MonsterRace: Can't save history: " + e.getMessage(), e);
}
}
/**
* Load current bets per lane ; initialize the map keys.
*/
protected void loadBets()
{
try (Connection con = DatabaseFactory.getConnection())
{
PreparedStatement statement = con.prepareStatement("SELECT * FROM mdt_bets");
ResultSet rset = statement.executeQuery();
while (rset.next())
{
setBetOnLane(rset.getInt("lane_id"), rset.getLong("bet"), false);
}
rset.close();
statement.close();
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, "MonsterRace: Can't load bets: " + e.getMessage(), e);
}
}
/**
* Save the current lane bet into database.
* @param lane : The lane to affect.
* @param sum : The sum to set.
*/
protected void saveBet(int lane, long sum)
{
try (Connection con = DatabaseFactory.getConnection())
{
PreparedStatement statement = con.prepareStatement("REPLACE INTO mdt_bets (lane_id, bet) VALUES (?,?)");
statement.setInt(1, lane);
statement.setLong(2, sum);
statement.execute();
statement.close();
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, "MonsterRace: Can't save bet: " + e.getMessage(), e);
}
}
/**
* Clear all lanes bets, either on database or Map.
*/
protected void clearBets()
{
for (int key : _betsPerLane.keySet())
{
_betsPerLane.put(key, 0L);
}
try (Connection con = DatabaseFactory.getConnection())
{
PreparedStatement statement = con.prepareStatement("UPDATE mdt_bets SET bet = 0");
statement.execute();
statement.close();
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, "MonsterRace: Can't clear bets: " + e.getMessage(), e);
}
}
/**
* Setup lane bet, based on previous value (if any).
* @param lane : The lane to edit.
* @param amount : The amount to add.
* @param saveOnDb : Should it be saved on db or not.
*/
public void setBetOnLane(int lane, long amount, boolean saveOnDb)
{
final long sum = (_betsPerLane.containsKey(lane)) ? _betsPerLane.get(lane) + amount : amount;
_betsPerLane.put(lane, sum);
if (saveOnDb)
{
saveBet(lane, sum);
}
}
/**
* Calculate odds for every lane, based on others lanes.
*/
protected void calculateOdds()
{
// Clear previous List holding old odds.
_odds.clear();
// Sort bets lanes per lane.
final Map<Integer, Long> sortedLanes = new TreeMap<>(_betsPerLane);
// Pass a first loop in order to calculate total sum of all lanes.
long sumOfAllLanes = 0;
for (long amount : sortedLanes.values())
{
sumOfAllLanes += amount;
}
// As we get the sum, we can now calculate the odd rate of each lane.
for (long amount : sortedLanes.values())
{
_odds.add((amount == 0) ? 0D : Math.max(1.25, (sumOfAllLanes * 0.7) / amount));
}
}
public Npc[] getMonsters()
{
return _monsters;
}
public int[][] getSpeeds()
{
return _speeds;
}
public int getFirstPlace()
{
return _first[0];
}
public int getSecondPlace()
{
return _second[0];
}
public MonRaceInfo getRacePacket()
{
return _packet;
}
public RaceState getCurrentRaceState()
{
return _state;
}
public int getRaceNumber()
{
return _raceNumber;
}
public List<HistoryInfo> getHistory()
{
return _history;
}
public List<Double> getOdds()
{
return _odds;
}
public static MonsterRace getInstance()
{
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder
{
protected static final MonsterRace INSTANCE = new MonsterRace();
}
}

View File

@@ -16,69 +16,26 @@
*/ */
package org.l2jmobius.gameserver.model.actor.instance; package org.l2jmobius.gameserver.model.actor.instance;
import java.util.Collection; import java.util.List;
import java.util.concurrent.ConcurrentHashMap; import java.util.Locale;
import org.l2jmobius.Config; import org.l2jmobius.Config;
import org.l2jmobius.commons.concurrent.ThreadPool; import org.l2jmobius.commons.util.StringUtil;
import org.l2jmobius.gameserver.MonsterRace;
import org.l2jmobius.gameserver.enums.InstanceType;
import org.l2jmobius.gameserver.idfactory.IdFactory; import org.l2jmobius.gameserver.idfactory.IdFactory;
import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.instancemanager.games.MonsterRace;
import org.l2jmobius.gameserver.instancemanager.games.MonsterRace.HistoryInfo;
import org.l2jmobius.gameserver.instancemanager.games.MonsterRace.RaceState;
import org.l2jmobius.gameserver.model.actor.Npc; import org.l2jmobius.gameserver.model.actor.Npc;
import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate; import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate;
import org.l2jmobius.gameserver.model.itemcontainer.Inventory;
import org.l2jmobius.gameserver.model.items.instance.ItemInstance; import org.l2jmobius.gameserver.model.items.instance.ItemInstance;
import org.l2jmobius.gameserver.network.SystemMessageId; import org.l2jmobius.gameserver.network.SystemMessageId;
import org.l2jmobius.gameserver.network.serverpackets.ActionFailed; import org.l2jmobius.gameserver.network.serverpackets.ActionFailed;
import org.l2jmobius.gameserver.network.serverpackets.DeleteObject;
import org.l2jmobius.gameserver.network.serverpackets.IClientOutgoingPacket;
import org.l2jmobius.gameserver.network.serverpackets.InventoryUpdate;
import org.l2jmobius.gameserver.network.serverpackets.MonRaceInfo;
import org.l2jmobius.gameserver.network.serverpackets.NpcHtmlMessage; import org.l2jmobius.gameserver.network.serverpackets.NpcHtmlMessage;
import org.l2jmobius.gameserver.network.serverpackets.PlaySound;
import org.l2jmobius.gameserver.network.serverpackets.SystemMessage; import org.l2jmobius.gameserver.network.serverpackets.SystemMessage;
import org.l2jmobius.gameserver.util.Broadcast;
public class RaceManagerInstance extends Npc public class RaceManagerInstance extends Npc
{ {
public static final int LANES = 8; protected static final int TICKET_PRICES[] =
public static final int WINDOW_START = 0;
private static Collection<RaceManagerInstance> _managers;
protected static int _raceNumber = 4;
// Time Constants
private static final long SECOND = 1000;
private static final long MINUTE = 60 * SECOND;
private static int _minutes = 5;
// States
private static final int ACCEPTING_BETS = 0;
private static final int WAITING = 1;
private static final int STARTING_RACE = 2;
private static final int RACE_END = 3;
private static int _state = RACE_END;
protected static final int[][] _codes =
{
{
-1,
0
},
{
0,
15322
},
{
13765,
-1
}
};
private static boolean _notInitialized = true;
protected static MonRaceInfo _packet;
protected static final int[] _cost =
{ {
100, 100,
500, 500,
@@ -93,223 +50,167 @@ public class RaceManagerInstance extends Npc
public RaceManagerInstance(NpcTemplate template) public RaceManagerInstance(NpcTemplate template)
{ {
super(template); super(template);
setInstanceType(InstanceType.RaceManagerInstance);
if (!Config.ALLOW_RACE)
{
return;
}
if (_notInitialized)
{
_notInitialized = false;
_managers = ConcurrentHashMap.newKeySet();
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKETS_ARE_NOW_AVAILABLE_FOR_MONSTER_RACE_S1), 0, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.NOW_SELLING_TICKETS_FOR_MONSTER_RACE_S1), 30 * SECOND, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKETS_ARE_NOW_AVAILABLE_FOR_MONSTER_RACE_S1), MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.NOW_SELLING_TICKETS_FOR_MONSTER_RACE_S1), MINUTE + (30 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S), 2 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S), 3 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S), 4 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S), 5 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S), 6 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKETS_SALES_ARE_CLOSED_FOR_MONSTER_RACE_S1_ODDS_ARE_POSTED), 7 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.MONSTER_RACE_S2_WILL_BEGIN_IN_S1_MINUTE_S), 7 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.MONSTER_RACE_S2_WILL_BEGIN_IN_S1_MINUTE_S), 8 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.MONSTER_RACE_S1_WILL_BEGIN_IN_30_SECONDS), (8 * MINUTE) + (30 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.MONSTER_RACE_S1_IS_ABOUT_TO_BEGIN_COUNTDOWN_IN_FIVE_SECONDS), (8 * MINUTE) + (50 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SECOND_S), (8 * MINUTE) + (55 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SECOND_S), (8 * MINUTE) + (56 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SECOND_S), (8 * MINUTE) + (57 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SECOND_S), (8 * MINUTE) + (58 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SECOND_S), (8 * MINUTE) + (59 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.THEY_RE_OFF), 9 * MINUTE, 10 * MINUTE);
}
_managers.add(this);
}
@Override
public boolean isAutoAttackable(Creature attacker)
{
if (attacker.isMonster())
{
return true;
}
return super.isAutoAttackable(attacker);
}
class Announcement implements Runnable
{
private final SystemMessageId _type;
public Announcement(SystemMessageId pType)
{
_type = pType;
}
@Override
public void run()
{
makeAnnouncement(_type);
}
}
public void makeAnnouncement(SystemMessageId type)
{
final SystemMessage sm = new SystemMessage(type);
switch (type.getId())
{
case 816: // SystemMessageId.TICKETS_ARE_NOW_AVAILABLE_FOR_MONSTER_RACE_S1
case 817: // SystemMessageId.NOW_SELLING_TICKETS_FOR_MONSTER_RACE_S1
{
if (_state != ACCEPTING_BETS)
{// LOGGER.info("Race Initializing");
_state = ACCEPTING_BETS;
startRace();
} // else{LOGGER.info("Race open");}
sm.addInt(_raceNumber);
break;
}
case 818: // SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S
case 820: // SystemMessageId.MONSTER_RACE_S2_WILL_BEGIN_IN_S1_MINUTE_S
case 823: // SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SECOND_S
{
sm.addInt(_minutes);
if (type.getId() == 820)
{
sm.addInt(_raceNumber);
}
_minutes--;
break;
}
case 819: // SystemMessageId.TICKETS_SALES_ARE_CLOSED_FOR_MONSTER_RACE_S1_ODDS_ARE_POSTED
{
// LOGGER.info("Sales closed");
sm.addInt(_raceNumber);
_state = WAITING;
_minutes = 2;
break;
}
case 821: // SystemMessageId.MONSTER_RACE_S1_WILL_BEGIN_IN_30_SECONDS
case 822: // SystemMessageId.MONSTER_RACE_S1_IS_ABOUT_TO_BEGIN_COUNTDOWN_IN_FIVE_SECONDS
case 825: // SystemMessageId.MONSTER_RACE_S1_IS_FINISHED
{
sm.addInt(_raceNumber);
_minutes = 5;
break;
}
case 826: // SystemMessageId.FIRST_PRIZE_GOES_TO_THE_PLAYER_IN_LANE_S1_SECOND_PRIZE_GOES_TO_THE_PLAYER_IN_LANE_S2
{
// LOGGER.info("Placing");
_state = RACE_END;
sm.addInt(MonsterRace.getInstance().getFirstPlace());
sm.addInt(MonsterRace.getInstance().getSecondPlace());
break;
}
}
// _logn.info("Counter: "+minutes);
// LOGGER.info("State: "+state);
broadcast(sm);
// LOGGER.info("Player's known: "+getKnownPlayers().size());
if (type == SystemMessageId.THEY_RE_OFF)
{
// LOGGER.info("Starting race");
_state = STARTING_RACE;
startRace();
_minutes = 5;
}
}
protected void broadcast(IClientOutgoingPacket pkt)
{
for (RaceManagerInstance manager : _managers)
{
if (!manager.isDead())
{
Broadcast.toKnownPlayers(manager, pkt);
}
}
}
public void sendMonsterInfo()
{
broadcast(_packet);
}
private void startRace()
{
final MonsterRace race = MonsterRace.getInstance();
if (_state == STARTING_RACE)
{
// state++;
broadcast(new PlaySound(1, "S_Race", 0, 0, 0, 0, 0));
broadcast(new PlaySound(0, "ItemSound2.race_start", 1, 121209259, 12125, 182487, -3559));
_packet = new MonRaceInfo(_codes[1][0], _codes[1][1], race.getMonsters(), race.getSpeeds());
sendMonsterInfo();
ThreadPool.schedule(new RunRace(), 5000);
}
else
{
// state++;
race.newRace();
race.newSpeeds();
_packet = new MonRaceInfo(_codes[0][0], _codes[0][1], race.getMonsters(), race.getSpeeds());
sendMonsterInfo();
}
} }
@Override @Override
public void onBypassFeedback(PlayerInstance player, String command) public void onBypassFeedback(PlayerInstance player, String command)
{ {
if (command.startsWith("BuyTicket") && (_state != ACCEPTING_BETS))
{
if (!Config.ALLOW_RACE)
{
return;
}
player.sendPacket(SystemMessageId.MONSTER_RACE_TICKETS_ARE_NO_LONGER_AVAILABLE);
command = "Chat 0";
}
if (command.startsWith("ShowOdds") && (_state == ACCEPTING_BETS))
{
if (!Config.ALLOW_RACE)
{
return;
}
player.sendPacket(SystemMessageId.MONSTER_RACE_PAYOUT_INFORMATION_IS_NOT_AVAILABLE_WHILE_TICKETS_ARE_BEING_SOLD);
command = "Chat 0";
}
if (command.startsWith("BuyTicket")) if (command.startsWith("BuyTicket"))
{ {
if (!Config.ALLOW_RACE) if (!Config.ALLOW_RACE || (MonsterRace.getInstance().getCurrentRaceState() != RaceState.ACCEPTING_BETS))
{ {
player.sendPacket(SystemMessageId.MONSTER_RACE_TICKETS_ARE_NO_LONGER_AVAILABLE);
super.onBypassFeedback(player, "Chat 0");
return; return;
} }
int val = Integer.parseInt(command.substring(10)); int val = Integer.parseInt(command.substring(10));
if (val == 0) if (val == 0)
{ {
player.setRace(0, 0); player.setRace(0, 0);
player.setRace(1, 0); player.setRace(1, 0);
} }
if (((val == 10) && (player.getRace(0) == 0)) || ((val == 20) && (player.getRace(0) == 0) && (player.getRace(1) == 0))) if (((val == 10) && (player.getRace(0) == 0)) || ((val == 20) && (player.getRace(0) == 0) && (player.getRace(1) == 0)))
{ {
val = 0; val = 0;
} }
showBuyTicket(player, val);
} String search, replace;
else if (command.equals("ShowOdds"))
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
if (val < 10)
{ {
if (!Config.ALLOW_RACE) html.setFile(player, getHtmlPath(getId(), 2, player));
for (int i = 0; i < 8; i++)
{
int n = i + 1;
search = "Mob" + n;
html.replace(search, MonsterRace.getInstance().getMonsters()[i].getTemplate().getName());
}
search = "No1";
if (val == 0)
{
html.replace(search, "");
}
else
{
html.replace(search, val);
player.setRace(0, val);
}
}
else if (val < 20)
{
if (player.getRace(0) == 0)
{ {
return; return;
} }
showOdds(player);
html.setFile(player, getHtmlPath(getId(), 3, player));
html.replace("0place", player.getRace(0));
search = "Mob1";
replace = MonsterRace.getInstance().getMonsters()[player.getRace(0) - 1].getTemplate().getName();
html.replace(search, replace);
search = "0adena";
if (val == 10)
{
html.replace(search, "");
}
else
{
html.replace(search, TICKET_PRICES[val - 11]);
player.setRace(1, val - 10);
}
}
else if (val == 20)
{
if ((player.getRace(0) == 0) || (player.getRace(1) == 0))
{
return;
}
html.setFile(player, getHtmlPath(getId(), 4, player));
html.replace("0place", player.getRace(0));
search = "Mob1";
replace = MonsterRace.getInstance().getMonsters()[player.getRace(0) - 1].getTemplate().getName();
html.replace(search, replace);
search = "0adena";
int price = TICKET_PRICES[player.getRace(1) - 1];
html.replace(search, price);
search = "0tax";
int tax = 0;
html.replace(search, tax);
search = "0total";
int total = price + tax;
html.replace(search, total);
}
else
{
if ((player.getRace(0) == 0) || (player.getRace(1) == 0))
{
return;
}
int ticket = player.getRace(0);
int priceId = player.getRace(1);
if (!player.reduceAdena("Race", TICKET_PRICES[priceId - 1], this, true))
{
return;
}
player.setRace(0, 0);
player.setRace(1, 0);
ItemInstance item = new ItemInstance(IdFactory.getInstance().getNextId(), 4443);
item.setCount(1);
item.setEnchantLevel(MonsterRace.getInstance().getRaceNumber());
item.setCustomType1(ticket);
item.setCustomType2(TICKET_PRICES[priceId - 1] / 100);
player.addItem("Race", item, player, false);
final SystemMessage msg = new SystemMessage(SystemMessageId.ACQUIRED_S1_S2);
msg.addInt(MonsterRace.getInstance().getRaceNumber());
msg.addItemName(4443);
player.sendPacket(msg);
// Refresh lane bet.
MonsterRace.getInstance().setBetOnLane(ticket, TICKET_PRICES[priceId - 1], true);
super.onBypassFeedback(player, "Chat 0");
return;
}
html.replace("1race", MonsterRace.getInstance().getRaceNumber());
html.replace("%objectId%", getObjectId());
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
else if (command.equals("ShowOdds"))
{
if (!Config.ALLOW_RACE || (MonsterRace.getInstance().getCurrentRaceState() == RaceState.ACCEPTING_BETS))
{
player.sendPacket(SystemMessageId.MONSTER_RACE_PAYOUT_INFORMATION_IS_NOT_AVAILABLE_WHILE_TICKETS_ARE_BEING_SOLD);
super.onBypassFeedback(player, "Chat 0");
return;
}
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
html.setFile(player, getHtmlPath(getId(), 5, player));
for (int i = 0; i < 8; i++)
{
final int n = i + 1;
html.replace("Mob" + n, MonsterRace.getInstance().getMonsters()[i].getTemplate().getName());
// Odd
final double odd = MonsterRace.getInstance().getOdds().get(i);
html.replace("Odd" + n, (odd > 0D) ? String.format(Locale.ENGLISH, "%.1f", odd) : "&$804;");
}
html.replace("1race", MonsterRace.getInstance().getRaceNumber());
html.replace("%objectId%", getObjectId());
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
} }
else if (command.equals("ShowInfo")) else if (command.equals("ShowInfo"))
{ {
@@ -317,269 +218,160 @@ public class RaceManagerInstance extends Npc
{ {
return; return;
} }
showMonsterInfo(player);
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
html.setFile(player, getHtmlPath(getId(), 6, player));
for (int i = 0; i < 8; i++)
{
int n = i + 1;
String search = "Mob" + n;
html.replace(search, MonsterRace.getInstance().getMonsters()[i].getTemplate().getName());
} }
else if (command.equals("calculateWin")) html.replace("%objectId%", getObjectId());
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
else if (command.equals("ShowTickets"))
{ {
if (!Config.ALLOW_RACE) if (!Config.ALLOW_RACE)
{ {
super.onBypassFeedback(player, "Chat 0");
return; return;
} }
// displayCalculateWinnings(player);
// Generate data.
final StringBuilder sb = new StringBuilder();
// Retrieve player's tickets.
for (ItemInstance ticket : player.getInventory().getAllItemsByItemId(4443))
{
// Don't list current race tickets.
if (ticket.getEnchantLevel() == MonsterRace.getInstance().getRaceNumber())
{
continue;
} }
else if (command.equals("viewHistory"))
StringUtil.append(sb, "<tr><td><a action=\"bypass -h npc_%objectId%_ShowTicket ", "" + ticket.getObjectId(), "\">", "" + ticket.getEnchantLevel(), " Race Number</a></td><td align=right><font color=\"LEVEL\">", "" + ticket.getCustomType1(), "</font> Number</td><td align=right><font color=\"LEVEL\">", "" + (ticket.getCustomType2() * 100), "</font> Adena</td></tr>");
}
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
html.setFile(player, getHtmlPath(getId(), 7, player));
html.replace("%tickets%", sb.toString());
html.replace("%objectId%", getObjectId());
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
else if (command.startsWith("ShowTicket"))
{
// Retrieve ticket objectId.
final int val = Integer.parseInt(command.substring(11));
if (!Config.ALLOW_RACE || (val == 0))
{
super.onBypassFeedback(player, "Chat 0");
return;
}
// Retrieve ticket on player's inventory.
final ItemInstance ticket = player.getInventory().getItemByObjectId(val);
if (ticket == null)
{
super.onBypassFeedback(player, "Chat 0");
return;
}
final int raceId = ticket.getEnchantLevel();
final int lane = ticket.getCustomType1();
final int bet = ticket.getCustomType2() * 100;
// Retrieve HistoryInfo for that race.
final HistoryInfo info = MonsterRace.getInstance().getHistory().get(raceId - 1);
if (info == null)
{
super.onBypassFeedback(player, "Chat 0");
return;
}
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
html.setFile(player, getHtmlPath(getId(), 8, player));
html.replace("%raceId%", raceId);
html.replace("%lane%", lane);
html.replace("%bet%", bet);
html.replace("%firstLane%", info.getFirst());
html.replace("%odd%", (lane == info.getFirst()) ? String.format(Locale.ENGLISH, "%.2f", info.getOddRate()) : "0.01");
html.replace("%objectId%", getObjectId());
html.replace("%ticketObjectId%", val);
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
else if (command.startsWith("CalculateWin"))
{
// Retrieve ticket objectId.
final int val = Integer.parseInt(command.substring(13));
if (!Config.ALLOW_RACE || (val == 0))
{
super.onBypassFeedback(player, "Chat 0");
return;
}
// Delete ticket on player's inventory.
final ItemInstance ticket = player.getInventory().getItemByObjectId(val);
if (ticket == null)
{
super.onBypassFeedback(player, "Chat 0");
return;
}
final int raceId = ticket.getEnchantLevel();
final int lane = ticket.getCustomType1();
final int bet = ticket.getCustomType2() * 100;
// Retrieve HistoryInfo for that race.
final HistoryInfo info = MonsterRace.getInstance().getHistory().get(raceId - 1);
if (info == null)
{
super.onBypassFeedback(player, "Chat 0");
return;
}
// Destroy the ticket.
if (player.destroyItem("MonsterTrack", ticket, this, true))
{
player.addAdena("MonsterTrack", (int) (bet * ((lane == info.getFirst()) ? info.getOddRate() : 0.01)), this, true);
}
super.onBypassFeedback(player, "Chat 0");
return;
}
else if (command.equals("ViewHistory"))
{ {
if (!Config.ALLOW_RACE) if (!Config.ALLOW_RACE)
{ {
super.onBypassFeedback(player, "Chat 0");
return; return;
} }
// displayHistory(player);
// Generate data.
final StringBuilder sb = new StringBuilder();
// Use whole history, pickup from 'last element' and stop at 'latest element - 7'.
final List<HistoryInfo> history = MonsterRace.getInstance().getHistory();
for (int i = history.size() - 1; i >= Math.max(0, history.size() - 7); i--)
{
final HistoryInfo info = history.get(i);
StringUtil.append(sb, "<tr><td><font color=\"LEVEL\">", "" + info.getRaceId(), "</font> th</td><td><font color=\"LEVEL\">", "" + info.getFirst(), "</font> Lane </td><td><font color=\"LEVEL\">", "" + info.getSecond(), "</font> Lane</td><td align=right><font color=00ffff>", String.format(Locale.ENGLISH, "%.2f", info.getOddRate()), "</font> Times</td></tr>");
}
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
html.setFile(player, getHtmlPath(getId(), 9, player));
html.replace("%infos%", sb.toString());
html.replace("%objectId%", getObjectId());
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
} }
else else
{ {
// getKnownList().removeKnownObject(player);
super.onBypassFeedback(player, command); super.onBypassFeedback(player, command);
} }
} }
public void showOdds(PlayerInstance player)
{
if (_state == ACCEPTING_BETS)
{
return;
}
final int npcId = getTemplate().getId();
String filename;
String search;
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
filename = getHtmlPath(npcId, 5, player);
html.setFile(player, filename);
for (int i = 0; i < 8; i++)
{
final int n = i + 1;
search = "Mob" + n;
html.replace(search, MonsterRace.getInstance().getMonsters()[i].getTemplate().getName());
}
html.replace("1race", String.valueOf(_raceNumber));
html.replace("%objectId%", String.valueOf(getObjectId()));
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
public void showMonsterInfo(PlayerInstance player)
{
final int npcId = getTemplate().getId();
String filename;
String search;
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
filename = getHtmlPath(npcId, 6, player);
html.setFile(player, filename);
for (int i = 0; i < 8; i++)
{
final int n = i + 1;
search = "Mob" + n;
html.replace(search, MonsterRace.getInstance().getMonsters()[i].getTemplate().getName());
}
html.replace("%objectId%", String.valueOf(getObjectId()));
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
public void showBuyTicket(PlayerInstance player, int value)
{
if (_state != ACCEPTING_BETS)
{
return;
}
final int npcId = getTemplate().getId();
SystemMessage sm;
String filename;
String search;
String replace;
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
if (value < 10)
{
filename = getHtmlPath(npcId, 2, player);
html.setFile(player, filename);
for (int i = 0; i < 8; i++)
{
final int n = i + 1;
search = "Mob" + n;
html.replace(search, MonsterRace.getInstance().getMonsters()[i].getTemplate().getName());
}
search = "No1";
if (value == 0)
{
html.replace(search, "");
}
else
{
html.replace(search, Integer.toString(value));
player.setRace(0, value);
}
}
else if (value < 20)
{
if (player.getRace(0) == 0)
{
return;
}
filename = getHtmlPath(npcId, 3, player);
html.setFile(player, filename);
html.replace("0place", Integer.toString(player.getRace(0)));
search = "Mob1";
replace = MonsterRace.getInstance().getMonsters()[player.getRace(0) - 1].getTemplate().getName();
html.replace(search, replace);
search = "0adena";
if (value == 10)
{
html.replace(search, "");
}
else
{
html.replace(search, Integer.toString(_cost[value - 11]));
player.setRace(1, value - 10);
}
}
else if (value == 20)
{
if ((player.getRace(0) == 0) || (player.getRace(1) == 0))
{
return;
}
filename = getHtmlPath(npcId, 4, player);
html.setFile(player, filename);
html.replace("0place", Integer.toString(player.getRace(0)));
search = "Mob1";
replace = MonsterRace.getInstance().getMonsters()[player.getRace(0) - 1].getTemplate().getName();
html.replace(search, replace);
search = "0adena";
final int price = _cost[player.getRace(1) - 1];
html.replace(search, Integer.toString(price));
search = "0tax";
final int tax = 0;
html.replace(search, Integer.toString(tax));
search = "0total";
final int total = price + tax;
html.replace(search, Integer.toString(total));
}
else
{
if ((player.getRace(0) == 0) || (player.getRace(1) == 0))
{
return;
}
final int ticket = player.getRace(0);
final int priceId = player.getRace(1);
if (!player.reduceAdena("Race", _cost[priceId - 1], this, true))
{
return;
}
player.setRace(0, 0);
player.setRace(1, 0);
sm = new SystemMessage(SystemMessageId.ACQUIRED_S1_S2);
sm.addInt(_raceNumber);
sm.addItemName(4443);
player.sendPacket(sm);
final ItemInstance item = new ItemInstance(IdFactory.getInstance().getNextId(), 4443);
item.setCount(1);
item.setEnchantLevel(_raceNumber);
item.setCustomType1(ticket);
item.setCustomType2(_cost[priceId - 1] / 100);
player.getInventory().addItem("Race", item, player, this);
final InventoryUpdate iu = new InventoryUpdate();
iu.addItem(item);
final ItemInstance adenaupdate = player.getInventory().getItemByItemId(Inventory.ADENA_ID);
iu.addModifiedItem(adenaupdate);
player.sendInventoryUpdate(iu);
return;
}
html.replace("1race", String.valueOf(_raceNumber));
html.replace("%objectId%", String.valueOf(getObjectId()));
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
public static class Race
{
private final Info[] _info;
public Race(Info[] pInfo)
{
_info = pInfo;
}
public Info getLaneInfo(int lane)
{
return _info[lane];
}
public class Info
{
private final int _id;
private final int _place;
private final int _odds;
private final int _payout;
public Info(int pId, int pPlace, int pOdds, int pPayout)
{
_id = pId;
_place = pPlace;
_odds = pOdds;
_payout = pPayout;
}
public int getId()
{
return _id;
}
public int getOdds()
{
return _odds;
}
public int getPayout()
{
return _payout;
}
public int getPlace()
{
return _place;
}
}
}
class RunRace implements Runnable
{
@Override
public void run()
{
_packet = new MonRaceInfo(_codes[2][0], _codes[2][1], MonsterRace.getInstance().getMonsters(), MonsterRace.getInstance().getSpeeds());
sendMonsterInfo();
ThreadPool.schedule(new RunEnd(), 30000);
}
}
class RunEnd implements Runnable
{
@Override
public void run()
{
makeAnnouncement(SystemMessageId.FIRST_PRIZE_GOES_TO_THE_PLAYER_IN_LANE_S1_SECOND_PRIZE_GOES_TO_THE_PLAYER_IN_LANE_S2);
makeAnnouncement(SystemMessageId.MONSTER_RACE_S1_IS_FINISHED);
_raceNumber++;
DeleteObject obj = null;
for (int i = 0; i < 8; i++)
{
obj = new DeleteObject(MonsterRace.getInstance().getMonsters()[i]);
broadcast(obj);
MonsterRace.getInstance().getMonsters()[i].deleteMe();
}
}
}
} }

View File

@@ -20,10 +20,12 @@ import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.l2jmobius.gameserver.enums.ChatType; import org.l2jmobius.gameserver.enums.ChatType;
import org.l2jmobius.gameserver.instancemanager.ZoneManager;
import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.World;
import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.actor.Creature;
import org.l2jmobius.gameserver.model.actor.Summon; import org.l2jmobius.gameserver.model.actor.Summon;
import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
import org.l2jmobius.gameserver.model.zone.ZoneType;
import org.l2jmobius.gameserver.network.serverpackets.CharInfo; import org.l2jmobius.gameserver.network.serverpackets.CharInfo;
import org.l2jmobius.gameserver.network.serverpackets.CreatureSay; import org.l2jmobius.gameserver.network.serverpackets.CreatureSay;
import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage; import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
@@ -191,4 +193,29 @@ public class Broadcast
{ {
toAllOnlinePlayers(new ExShowScreenMessage(text, 10000)); toAllOnlinePlayers(new ExShowScreenMessage(text, 10000));
} }
/**
* Send a packet to all players in a specific zone type.
* @param <T> ZoneType.
* @param zoneType : The zone type to send packets.
* @param packets : The packets to send.
*/
public static <T extends ZoneType> void toAllPlayersInZoneType(Class<T> zoneType, IClientOutgoingPacket... packets)
{
for (ZoneType zone : ZoneManager.getInstance().getAllZones(zoneType))
{
for (Creature creature : zone.getCharactersInside())
{
if (creature == null)
{
continue;
}
for (IClientOutgoingPacket packet : packets)
{
creature.sendPacket(packet);
}
}
}
}
} }

View File

@@ -0,0 +1,15 @@
CREATE TABLE IF NOT EXISTS `mdt_bets` (
`lane_id` INT(1) DEFAULT 0,
`bet` INT DEFAULT 0,
PRIMARY KEY (`lane_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;
INSERT INTO `mdt_bets` VALUES
('1','0'),
('2','0'),
('3','0'),
('4','0'),
('5','0'),
('6','0'),
('7','0'),
('8','0');

View File

@@ -0,0 +1,7 @@
CREATE TABLE IF NOT EXISTS `mdt_history` (
`race_id` MEDIUMINT DEFAULT 0,
`first` INT(1) DEFAULT 0,
`second` INT(1) DEFAULT 0,
`odd_rate` DOUBLE(10,2) DEFAULT 0,
PRIMARY KEY (`race_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;

View File

@@ -416,9 +416,6 @@
<admin command="admin_mobgroup_attackgrp" accessLevel="100" /> <admin command="admin_mobgroup_attackgrp" accessLevel="100" />
<admin command="admin_mobgroup_invul" accessLevel="100" /> <admin command="admin_mobgroup_invul" accessLevel="100" />
<!-- ADMIN MONSTER RACE -->
<admin command="admin_mons" accessLevel="100" />
<!-- ADMIN ONLINE --> <!-- ADMIN ONLINE -->
<admin command="admin_online" accessLevel="100" /> <admin command="admin_online" accessLevel="100" />

View File

@@ -1,19 +1,22 @@
<html><body><br><br> <html><body>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 0">Back</Button><br> <br>
In a monster race eight monsters race at the same time. If you guess which monster is expected to finish first and buy that monster's race ticket, based upon the odds to win you will receive an appropriate amount of Adena.<br> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 0">Go Back</Button>
<font color="LEVEL">How the Race Works</font>Monster races run every 20 minutes in real time.<br> <br>
<font color="LEVEL">How to Participate</font><br> In a monster race eight monsters race at the same time. If you guess which monster is expected to finish first and buy that monster's race ticket, based upon the odds to win you will receive an appropriate amount of adena.<br>
You can purchase a monster race ticket through a race manager NPC. Only between 18 minutes before the start of a race until 3 minutes before the race begins can you buy a ticket for the applicable race. When you purchase a ticket through a race manager, you can find out the names of the participating monsters and their current statistics. Three minutes before a race starts, the race manager will stop ticket sales and announce the monsters' individual odds for the current race.<br> <font color="LEVEL">Racing Schedule</font><br>
<font color="LEVEL">Betting</font><br> Monster races every 20 minutes in real time.<br>
There are two types of race betting. The Win bet means that you back a particular monster to win 1st place. The Place bet means that you will have to guess the 1st and 2nd place monsters, regardless of who finishes first or second.<br> <font color="LEVEL">Method of Race Participation</font><br>
You can purchase a monster race ticket through a race manager NPC. Only between 18 minutes before the start of a race until 3 minutes before the race begins you can buy a ticket for the applicable race. When you purchase a ticket through a conversation with a race manager, you can find out the names of participating monsters in the race and their current statistics. Five minutes before a race starts, the race manager will stop ticket sales and announce monsters' individual odds rate for the current race.<br>
<font color="LEVEL">Types of Race</font><br>
There are two types of race betting. The Win type betting means that you back a monster that will win the first prize. Place type of betting means that you will have to guess the 1st and 2nd winning monsters, regardless of who finishes first or second.<br>
<font color="00FFFF">Win</font><br1> <font color="00FFFF">Win</font><br1>
* Among the entire betting amount for Win betting, after subtracting the race track's own share of profit, the amount left will be your total winnings. Three minutes before the race, each monster's expected odds are announced. Each Win ticket's dividend amount is calculated by multiplying the odds by the price of a ticket. The odds are never less than 1.<br1> * Among the entire betting amount for Win betting, after subtracting the race track's own share of profit, the amount left will be your total winnings. Three minutes before the race each monster's expected odds are announced. Each Win ticket's dividend amount is calculated by multiplying the odds by the price of a ticket. The odds rate is never less than 1.<br1>
* The prize money does not get transferred to the next race.<br> * The prize money does not get transferred to the next race.<br>
<font color="00FFFF">Place</font><br1> <font color="00FFFF">Place</font><br1>
* Place betting is a way of betting when you correctly guess the two monsters who will finish first and second regardless of their order. The method of calculating the dividend amount is the same as Win betting.<br1> * Place betting is a way of betting when you correctly guess the two monsters who will finish first and second regardless of their order. The method of calculating dividend amount is the same as Win betting.<br>
* Currently place betting is not available.<br> * Currently place betting is not offered.<br>
<font color="LEVEL">End of Race</font><br> <font color="LEVEL">End of Race</font><br>
When a race is over, the race manager will shout the results of the race. You can trade your ticket for Adena by speaking to a race manager NPC. Check the Winning Information Data to find out the types of bets and total winnings for the race you bet on, as well as your win status and prize amount. If you won, you can receive your prize money thorough a race manager NPC. If you did not win, a race manager will buy back your ticket for a small amount of money.<br> When a race is over, the race manager will shout out the results of the race. Through a conversation with a race manager NPC, you can trade your ticket for adena. By looking up on the Winning Information Data, you can find out the types of tickets and quantity of the winnings in the race you betted on and your win status and prize amount you won. If you won, you can receive your prize money thorough a race manager NPC. If you did not win, a race manager will buy back your ticket by paying you a very small amount of money.<br>
If a server goes down while tickets are being sold, or while a race is in progress, the race will be cancelled. You can receive a full refund for your ticket for a cancelled race through a race manager.<br> If a server goes down while tickets are being sold, or while a race is in progress, the race will be cancelled. You can receive a full refund for your ticket for a cancelled race through a race manager.<br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 0">Back</Button> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 0">Go Back</Button>
</body></html> </body></html>

View File

@@ -3,6 +3,7 @@
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=1 height=3> <img src="L2UI.SquareBlank" width=1 height=3>
</center> </center>
<table width=270 border=0 bgcolor="111111"> <table width=270 border=0 bgcolor="111111">
<tr> <tr>
<td fixwidth=60>Lane Selection</td> <td fixwidth=60>Lane Selection</td>
@@ -11,6 +12,7 @@
<td fixwidth=30 align=right>Odds</td> <td fixwidth=30 align=right>Odds</td>
</tr> </tr>
</table> </table>
<table width=270 border=0> <table width=270 border=0>
<tr> <tr>
<td fixwidth=13><img src="L2UI.rightBtn2" width=14 height=13></td> <td fixwidth=13><img src="L2UI.rightBtn2" width=14 height=13></td>
@@ -20,18 +22,21 @@
<td fixwidth=30 align=right><font color="00ffff">13%</font></td> <td fixwidth=30 align=right><font color="00ffff">13%</font></td>
</tr> </tr>
</table> </table>
<table width=270 border=0> <table width=270 border=0>
<tr><td width=100>Select Bet Amount</td> <tr><td width=100>Select Bet Amount</td>
<td width=170> <td width=170>
<a action="bypass -h npc_%objectId%_BuyTicket 11" >100 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 12" >500 Adena</a><br1> <a action="bypass -h npc_%objectId%_BuyTicket 11" >100 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 12" >500 Adena</a> <br1>
<a action="bypass -h npc_%objectId%_BuyTicket 13" >1,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 14" >5,000 Adena</a><br1> <a action="bypass -h npc_%objectId%_BuyTicket 13" >1,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 14" >5,000 Adena</a> <br1>
<a action="bypass -h npc_%objectId%_BuyTicket 15" >10,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 16" >20,000 Adena</a><br1> <a action="bypass -h npc_%objectId%_BuyTicket 15" >10,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 16" >20,000 Adena</a> <br1>
<a action="bypass -h npc_%objectId%_BuyTicket 17" >50,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 18" >100,000 Adena</a><br1> <a action="bypass -h npc_%objectId%_BuyTicket 17" >50,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 18" >100,000 Adena</a> <br1>
</td></tr> </td></tr>
</table> </table>
<img src="L2UI.SquareBlank" width=270 height=10> <img src="L2UI.SquareBlank" width=270 height=10>
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=270 height=5> <img src="L2UI.SquareBlank" width=270 height=5>
<table width=270 border=0 cellpadding=0 cellspacing=0> <table width=270 border=0 cellpadding=0 cellspacing=0>
<tr> <tr>
<td width=70 height=20><button value="Select Lane" action="bypass -h npc_%objectId%_BuyTicket 0" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td> <td width=70 height=20><button value="Select Lane" action="bypass -h npc_%objectId%_BuyTicket 0" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td>
@@ -39,4 +44,5 @@
<td width=70 align=right><button value="Confirm" action="bypass -h npc_%objectId%_BuyTicket 20" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td> <td width=70 align=right><button value="Confirm" action="bypass -h npc_%objectId%_BuyTicket 20" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td>
</tr> </tr>
</table> </table>
</body></html> </body></html>

View File

@@ -11,6 +11,7 @@
<td fixwidth=30 align=right>Odds</td> <td fixwidth=30 align=right>Odds</td>
</tr> </tr>
</table> </table>
<table width=270 border=0> <table width=270 border=0>
<tr> <tr>
<td fixwidth=13><img src="L2UI.rightBtn2" width=14 height=13></td> <td fixwidth=13><img src="L2UI.rightBtn2" width=14 height=13></td>
@@ -20,6 +21,7 @@
<td fixwidth=30 align=right><font color="00ffff">13%</font></td> <td fixwidth=30 align=right><font color="00ffff">13%</font></td>
</tr> </tr>
</table> </table>
<table width=270 border=0 bgcolor=111111> <table width=270 border=0 bgcolor=111111>
<tr> <tr>
<td fixwidth=60>Bet Amount </td> <td fixwidth=60>Bet Amount </td>
@@ -37,11 +39,13 @@
<td>Total </td> <td>Total </td>
<td><font color="LEVEL">0total</font> Adena</td> <td><font color="LEVEL">0total</font> Adena</td>
</tr> </tr>
</table><br> </table>
<br>
<center>If the information is correct, please click Confirm.</center><br> <center>If the information is correct, please click Confirm.</center><br>
<img src="L2UI.SquareBlank" width=270 height=10> <img src="L2UI.SquareBlank" width=270 height=10>
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=270 height=5> <img src="L2UI.SquareBlank" width=270 height=5>
<table width=270 border=0 cellpadding=0 cellspacing=0> <table width=270 border=0 cellpadding=0 cellspacing=0>
<tr> <tr>
<td width=70 height=20><button value=&$141; action="bypass -h npc_%objectId%_Chat 0" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td> <td width=70 height=20><button value=&$141; action="bypass -h npc_%objectId%_Chat 0" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td>

View File

@@ -15,49 +15,49 @@
<tr> <tr>
<td fixwidth=40>No.1</td> <td fixwidth=40>No.1</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob1</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob1</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd1</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.2</td> <td fixwidth=40>No.2</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob2</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob2</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd2</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.3</td> <td fixwidth=40>No.3</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob3</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob3</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd3</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.4</td> <td fixwidth=40>No.4</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob4</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob4</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd4</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.5</td> <td fixwidth=40>No.5</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob5</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob5</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd5</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.6</td> <td fixwidth=40>No.6</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob6</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob6</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd6</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.7</td> <td fixwidth=40>No.7</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob7</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob7</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd7</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.8</td> <td fixwidth=40>No.8</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob8</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob8</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd8</font> </td>
</tr> </tr>
</table> </table>
<img src="L2UI.SquareBlank" width=270 height=10> <img src="L2UI.SquareBlank" width=270 height=10>

View File

@@ -3,22 +3,12 @@
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=1 height=3> <img src="L2UI.SquareBlank" width=1 height=3>
<table width=270 border=0 bgcolor=111111> <table width=270 border=0 bgcolor=111111>
<tr><td fixwidth=60>Winnings for a Race.</td><td fixwidth=60 align=right>Lane</td><td fixwidth=160 align=right>Bet Amount</td></tr> <tr><td fixwidth=70>Winnings for a Race.</td><td fixwidth=70 align=right>Lane</td><td fixwidth=130 align=right>Bet Amount</td></tr>
<tr><td fixwidth=60 fixheight=1></td><td fixwidth=60></td><td fixwidth=160></td></tr> <tr><td fixwidth=70 fixheight=1></td><td fixwidth=70></td><td fixwidth=130></td></tr>
</table> </table>
<table width=270 border=0> <table width=270 border=0>
<tr> %tickets%
<td><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 8">1 Race Number</Button></td>
<td align=right><font color="LEVEL">1</font> Number</td>
<td align=right><font color="LEVEL">100</font> Adena</td>
</tr>
</table> </table>
<img src="L2UI.SquareWhite" width=270 height=1>
<table width=270 border=0>
<tr><td width=90><Button ALIGN=LEFT ICON="NORMAL" action="bypass _mrsl01_1">Previous List</Button></td><td width=90 align=center>1 Page </td><td width=90 align=right><Button ALIGN=LEFT ICON="NORMAL" action="bypass _mrsl01_2"> Next List</Button></td></tr>
</table>
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=270 height=5> <img src="L2UI.SquareBlank" width=270 height=5>
<table width=270 border=0 cellpadding=0 cellspacing=0> <table width=270 border=0 cellpadding=0 cellspacing=0>

View File

@@ -4,11 +4,11 @@
<img src="L2UI.SquareBlank" width=1 height=3> <img src="L2UI.SquareBlank" width=1 height=3>
<table width=270 border=0> <table width=270 border=0>
<tr> <tr>
<td width=230>Race #19090<br> <td width=230>Race #%raceId%<br>
Lane 1<br> Lane: %lane%<br>
Bet: 100 adena<br> Bet: %bet% Adena<br>
1st place: Lane [4]<br> 1st place: Lane [%firstLane%]<br>
Odds:[0.01] Odds: [%odd%]
</td> </td>
</tr> </tr>
</table> </table>
@@ -18,8 +18,8 @@ Odds:[0.01]
<table width=270 border=0 cellpadding=0 cellspacing=0> <table width=270 border=0 cellpadding=0 cellspacing=0>
<tr> <tr>
<td width=90 height=20><button value="Previous" action="bypass -h npc_%objectId%_Chat 7" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td> <td width=90 height=20><button value="Previous" action="bypass -h npc_%objectId%_ShowTickets" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td>
<td width=90></td> <td width=90></td>
<td width=90 align=right><button value="Calculate" action="bypass _mrsl03_30635184" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td> <td width=90 align=right><button value="Calculate" action="bypass -h npc_%objectId%_CalculateWin %ticketObjectId%" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td>
</tr></table> </tr></table>
</body></html> </body></html>

View File

@@ -7,41 +7,7 @@
</table> </table>
<table width=270 border=0> <table width=270 border=0>
<tr><td fixwidth=60 fixheight=1></td><td fixwidth=70></td><td fixwidth=70></td><td fixwidth=70></td></tr> <tr><td fixwidth=60 fixheight=1></td><td fixwidth=70></td><td fixwidth=70></td><td fixwidth=70></td></tr>
<tr> %infos%
<td><font color="LEVEL">7</font> th</td>
<td><font color="LEVEL">8</font> Lane </td><td><font color="LEVEL">1</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">6</font> th</td>
<td><font color="LEVEL">4</font> Lane </td><td><font color="LEVEL">8</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">5</font> th</td>
<td><font color="LEVEL">7</font> Lane </td><td><font color="LEVEL">2</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">4</font> th</td>
<td><font color="LEVEL">6</font> Lane </td><td><font color="LEVEL">8</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">3</font> th</td>
<td><font color="LEVEL">4</font> Lane </td><td><font color="LEVEL">1</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">2</font> th</td>
<td><font color="LEVEL">1</font> Lane </td><td><font color="LEVEL">8</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">1</font> th</td>
<td><font color="LEVEL">7</font> Lane </td><td><font color="LEVEL">6</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
</table> </table>
<img src="L2UI.SquareBlank" width=270 height=10> <img src="L2UI.SquareBlank" width=270 height=10>
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>

View File

@@ -1,15 +1,15 @@
<html>
<body>
<center>Monster Race Manager</center> <center>Monster Race Manager</center>
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareBlank" width=1 height=3> <img src="L2UI.SquareWhite" width=270 height=1>
<table width=270 border=0> <img src="L2UI.SquareBlank" width=1 height=3>
<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 1">Monster Race Help</Button></td></tr> <br>
<!--<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_ShowOdds">View Odds</Button></td></tr>--> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 1">Monster Race Help</Button>
<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_ShowInfo">View Monster Information</Button></td></tr> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_ShowOdds">View Odds</Button>
<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_BuyTicket 0">Purchase Ticket</Button></td></tr> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_ShowInfo">View Monster Information</Button>
<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 7">Calculate Winnings</Button></td></tr> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_BuyTicket 0">Purchase Ticket</Button>
<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 9">View Past Results</Button></td></tr> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_ShowTickets">Calculate Winnings</Button>
<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Quest TeleportToRaceTrack" msg="1298;Monster Race Track">Exit the Monster Race Track</Button></td></tr> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_ViewHistory">View Past Results</Button>
</table> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Quest TeleportToRaceTrack" msg="1298;Monster Race Track">Exit the monster race track.</Button>
<img src="L2UI.SquareBlank" width=270 height=10> <img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareBlank" width=270 height=5> <img src="L2UI.SquareBlank" width=270 height=10>
<img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=270 height=5>
</body></html> </body></html>

View File

@@ -105,7 +105,6 @@ import handlers.admincommandhandlers.AdminMenu;
import handlers.admincommandhandlers.AdminMessages; import handlers.admincommandhandlers.AdminMessages;
import handlers.admincommandhandlers.AdminMissingHtmls; import handlers.admincommandhandlers.AdminMissingHtmls;
import handlers.admincommandhandlers.AdminMobGroup; import handlers.admincommandhandlers.AdminMobGroup;
import handlers.admincommandhandlers.AdminMonsterRace;
import handlers.admincommandhandlers.AdminOlympiad; import handlers.admincommandhandlers.AdminOlympiad;
import handlers.admincommandhandlers.AdminOnline; import handlers.admincommandhandlers.AdminOnline;
import handlers.admincommandhandlers.AdminPForge; import handlers.admincommandhandlers.AdminPForge;
@@ -442,7 +441,6 @@ public class MasterHandler
AdminMessages.class, AdminMessages.class,
AdminMissingHtmls.class, AdminMissingHtmls.class,
AdminMobGroup.class, AdminMobGroup.class,
AdminMonsterRace.class,
AdminOlympiad.class, AdminOlympiad.class,
AdminOnline.class, AdminOnline.class,
AdminPathNode.class, AdminPathNode.class,

View File

@@ -1,166 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package handlers.admincommandhandlers;
import org.l2jmobius.commons.concurrent.ThreadPool;
import org.l2jmobius.gameserver.MonsterRace;
import org.l2jmobius.gameserver.handler.IAdminCommandHandler;
import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
import org.l2jmobius.gameserver.network.SystemMessageId;
import org.l2jmobius.gameserver.network.serverpackets.DeleteObject;
import org.l2jmobius.gameserver.network.serverpackets.MonRaceInfo;
import org.l2jmobius.gameserver.network.serverpackets.PlaySound;
import org.l2jmobius.gameserver.network.serverpackets.SystemMessage;
/**
* This class handles following admin commands: - invul = turns invulnerability on/off
* @version $Revision: 1.1.6.4 $ $Date: 2007/07/31 10:06:00 $
*/
public class AdminMonsterRace implements IAdminCommandHandler
{
private static final String[] ADMIN_COMMANDS =
{
"admin_mons"
};
static int state = -1;
@Override
public boolean useAdminCommand(String command, PlayerInstance activeChar)
{
if (command.equalsIgnoreCase("admin_mons"))
{
handleSendPacket(activeChar);
}
return true;
}
@Override
public String[] getAdminCommandList()
{
return ADMIN_COMMANDS;
}
private void handleSendPacket(PlayerInstance activeChar)
{
/*
* -1 0 to initialize the race 0 15322 to start race 13765 -1 in middle of race -1 0 to end the race 8003 to 8027
*/
final int[][] codes =
{
{
-1,
0
},
{
0,
15322
},
{
13765,
-1
},
{
-1,
0
}
};
final MonsterRace race = MonsterRace.getInstance();
if (state == -1)
{
state++;
race.newRace();
race.newSpeeds();
final MonRaceInfo spk = new MonRaceInfo(codes[state][0], codes[state][1], race.getMonsters(), race.getSpeeds());
activeChar.sendPacket(spk);
activeChar.broadcastPacket(spk);
}
else if (state == 0)
{
state++;
final SystemMessage sm = new SystemMessage(SystemMessageId.THEY_RE_OFF);
sm.addInt(0);
activeChar.sendPacket(sm);
final PlaySound sRace = new PlaySound(1, "S_Race", 0, 0, 0, 0, 0);
activeChar.sendPacket(sRace);
activeChar.broadcastPacket(sRace);
final PlaySound sRace2 = new PlaySound(0, "ItemSound2.race_start", 1, 121209259, 12125, 182487, -3559);
activeChar.sendPacket(sRace2);
activeChar.broadcastPacket(sRace2);
final MonRaceInfo spk = new MonRaceInfo(codes[state][0], codes[state][1], race.getMonsters(), race.getSpeeds());
activeChar.sendPacket(spk);
activeChar.broadcastPacket(spk);
ThreadPool.schedule(new RunRace(codes, activeChar), 5000);
}
}
private class RunRace implements Runnable
{
private final int[][] codes;
private final PlayerInstance activeChar;
public RunRace(int[][] pCodes, PlayerInstance pActiveChar)
{
codes = pCodes;
activeChar = pActiveChar;
}
@Override
public void run()
{
// int[][] speeds1 = MonsterRace.getInstance().getSpeeds();
// MonsterRace.getInstance().newSpeeds();
// int[][] speeds2 = MonsterRace.getInstance().getSpeeds();
/*
* int[] speed = new int[8]; for (int i=0; i<8; i++) { for (int j=0; j<20; j++) { //LOGGER.info("Adding "+speeds1[i][j] +" and "+ speeds2[i][j]); speed[i] += (speeds1[i][j]*1); // + (speeds2[i][j]*1); } LOGGER.info("Total speed for "+(i+1)+" = "+speed[i]); }
*/
final MonRaceInfo spk = new MonRaceInfo(codes[2][0], codes[2][1], MonsterRace.getInstance().getMonsters(), MonsterRace.getInstance().getSpeeds());
activeChar.sendPacket(spk);
activeChar.broadcastPacket(spk);
ThreadPool.schedule(new RunEnd(activeChar), 30000);
}
}
private static class RunEnd implements Runnable
{
private final PlayerInstance activeChar;
public RunEnd(PlayerInstance pActiveChar)
{
activeChar = pActiveChar;
}
@Override
public void run()
{
DeleteObject obj = null;
for (int i = 0; i < 8; i++)
{
obj = new DeleteObject(MonsterRace.getInstance().getMonsters()[i]);
activeChar.sendPacket(obj);
activeChar.broadcastPacket(obj);
}
state = -1;
}
}
}

View File

@@ -144,6 +144,7 @@ import org.l2jmobius.gameserver.instancemanager.SiegeGuardManager;
import org.l2jmobius.gameserver.instancemanager.SiegeManager; import org.l2jmobius.gameserver.instancemanager.SiegeManager;
import org.l2jmobius.gameserver.instancemanager.WalkingManager; import org.l2jmobius.gameserver.instancemanager.WalkingManager;
import org.l2jmobius.gameserver.instancemanager.ZoneManager; import org.l2jmobius.gameserver.instancemanager.ZoneManager;
import org.l2jmobius.gameserver.instancemanager.games.MonsterRace;
import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.World;
import org.l2jmobius.gameserver.model.entity.Hero; import org.l2jmobius.gameserver.model.entity.Hero;
import org.l2jmobius.gameserver.model.events.EventDispatcher; import org.l2jmobius.gameserver.model.events.EventDispatcher;
@@ -404,10 +405,8 @@ public class GameServer
ItemsAutoDestroy.getInstance(); ItemsAutoDestroy.getInstance();
} }
if (Config.ALLOW_RACE)
{
MonsterRace.getInstance(); MonsterRace.getInstance();
}
TaskManager.getInstance(); TaskManager.getInstance();
AntiFeedManager.getInstance().registerEvent(AntiFeedManager.GAME_ID); AntiFeedManager.getInstance().registerEvent(AntiFeedManager.GAME_ID);

View File

@@ -1,140 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.data.xml.impl.NpcData;
import org.l2jmobius.gameserver.model.actor.Npc;
import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate;
public class MonsterRace
{
protected static final Logger LOGGER = Logger.getLogger(MonsterRace.class.getName());
private final Npc[] _monsters;
private int[][] _speeds;
private final int[] _first;
private final int[] _second;
protected MonsterRace()
{
_monsters = new Npc[8];
_speeds = new int[8][20];
_first = new int[2];
_second = new int[2];
}
public void newRace()
{
int random = 0;
for (int i = 0; i < 8; i++)
{
final int id = 31003;
random = Rnd.get(24);
while (true)
{
for (int j = i - 1; j >= 0; j--)
{
if (_monsters[j].getTemplate().getId() == (id + random))
{
random = Rnd.get(24);
}
}
break;
}
try
{
final NpcTemplate template = NpcData.getInstance().getTemplate(id + random);
_monsters[i] = (Npc) Class.forName("org.l2jmobius.gameserver.model.actor.instance." + template.getType() + "Instance").getConstructors()[0].newInstance(template);
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Unable to create monster!", e);
}
}
newSpeeds();
}
public void newSpeeds()
{
_speeds = new int[8][20];
int total = 0;
_first[1] = 0;
_second[1] = 0;
for (int i = 0; i < 8; i++)
{
total = 0;
for (int j = 0; j < 20; j++)
{
_speeds[i][j] = j == 19 ? 100 : Rnd.get(60) + 65;
total += _speeds[i][j];
}
if (total >= _first[1])
{
_second[0] = _first[0];
_second[1] = _first[1];
_first[0] = 8 - i;
_first[1] = total;
}
else if (total >= _second[1])
{
_second[0] = 8 - i;
_second[1] = total;
}
}
}
/**
* @return Returns the monsters.
*/
public Npc[] getMonsters()
{
return _monsters;
}
/**
* @return Returns the speeds.
*/
public int[][] getSpeeds()
{
return _speeds;
}
public int getFirstPlace()
{
return _first[0];
}
public int getSecondPlace()
{
return _second[0];
}
public static MonsterRace getInstance()
{
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder
{
protected static final MonsterRace INSTANCE = new MonsterRace();
}
}

View File

@@ -0,0 +1,623 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.instancemanager.games;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.l2jmobius.Config;
import org.l2jmobius.commons.concurrent.ThreadPool;
import org.l2jmobius.commons.database.DatabaseFactory;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.data.xml.impl.NpcData;
import org.l2jmobius.gameserver.model.actor.Npc;
import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate;
import org.l2jmobius.gameserver.model.zone.type.DerbyTrackZone;
import org.l2jmobius.gameserver.network.SystemMessageId;
import org.l2jmobius.gameserver.network.serverpackets.DeleteObject;
import org.l2jmobius.gameserver.network.serverpackets.MonRaceInfo;
import org.l2jmobius.gameserver.network.serverpackets.PlaySound;
import org.l2jmobius.gameserver.network.serverpackets.SystemMessage;
import org.l2jmobius.gameserver.util.Broadcast;
public class MonsterRace
{
protected static final Logger LOGGER = Logger.getLogger(MonsterRace.class.getName());
public static enum RaceState
{
ACCEPTING_BETS,
WAITING,
STARTING_RACE,
RACE_END
}
protected static final PlaySound SOUND_1 = new PlaySound(1, "S_Race", 0, 0, 0, 0, 0);
protected static final PlaySound SOUND_2 = new PlaySound("ItemSound2.race_start");
protected static final int[][] CODES =
{
{
-1,
0
},
{
0,
15322
},
{
13765,
-1
}
};
protected final List<Integer> _npcTemplates = new ArrayList<>(); // List holding npc templates, shuffled on a new race.
protected final List<HistoryInfo> _history = new ArrayList<>(); // List holding old race records.
protected final Map<Integer, Long> _betsPerLane = new ConcurrentHashMap<>(); // Map holding all bets for each lane ; values setted to 0 after every race.
protected final List<Double> _odds = new ArrayList<>(); // List holding sorted odds per lane ; cleared at new odds calculation.
protected int _raceNumber = 1;
protected int _finalCountdown = 0;
protected RaceState _state = RaceState.RACE_END;
protected MonRaceInfo _packet;
private final Npc[] _monsters = new Npc[8];
private int[][] _speeds = new int[8][20];
private final int[] _first = new int[2];
private final int[] _second = new int[2];
protected MonsterRace()
{
if (!Config.ALLOW_RACE)
{
return;
}
// Feed _history with previous race results.
loadHistory();
// Feed _betsPerLane with stored informations on bets.
loadBets();
// Feed _npcTemplates, we will only have to shuffle it when needed.
for (int i = 31003; i < 31027; i++)
{
_npcTemplates.add(i);
}
ThreadPool.scheduleAtFixedRate(new Announcement(), 0, 1000);
}
public static class HistoryInfo
{
private final int _raceId;
private int _first;
private int _second;
private double _oddRate;
public HistoryInfo(int raceId, int first, int second, double oddRate)
{
_raceId = raceId;
_first = first;
_second = second;
_oddRate = oddRate;
}
public int getRaceId()
{
return _raceId;
}
public int getFirst()
{
return _first;
}
public int getSecond()
{
return _second;
}
public double getOddRate()
{
return _oddRate;
}
public void setFirst(int first)
{
_first = first;
}
public void setSecond(int second)
{
_second = second;
}
public void setOddRate(double oddRate)
{
_oddRate = oddRate;
}
}
private class Announcement implements Runnable
{
public Announcement()
{
}
@Override
public void run()
{
if (_finalCountdown > 1200)
{
_finalCountdown = 0;
}
switch (_finalCountdown)
{
case 0:
{
newRace();
newSpeeds();
_state = RaceState.ACCEPTING_BETS;
_packet = new MonRaceInfo(CODES[0][0], CODES[0][1], getMonsters(), getSpeeds());
final SystemMessage msg = new SystemMessage(SystemMessageId.TICKETS_ARE_NOW_AVAILABLE_FOR_MONSTER_RACE_S1);
msg.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, _packet, msg);
break;
}
case 30: // 30 sec
case 60: // 1 min
case 90: // 1 min 30 sec
case 120: // 2 min
case 150: // 2 min 30
case 180: // 3 min
case 210: // 3 min 30
case 240: // 4 min
case 270: // 4 min 30 sec
case 330: // 5 min 30 sec
case 360: // 6 min
case 390: // 6 min 30 sec
case 420: // 7 min
case 450: // 7 min 30
case 480: // 8 min
case 510: // 8 min 30
case 540: // 9 min
case 570: // 9 min 30 sec
case 630: // 10 min 30 sec
case 660: // 11 min
case 690: // 11 min 30 sec
case 720: // 12 min
case 750: // 12 min 30
case 780: // 13 min
case 810: // 13 min 30
case 870: // 14 min 30 sec
{
final SystemMessage msg = new SystemMessage(SystemMessageId.TICKETS_ARE_NOW_AVAILABLE_FOR_MONSTER_RACE_S1);
msg.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg);
break;
}
case 300: // 5 min
{
final SystemMessage msg = new SystemMessage(SystemMessageId.NOW_SELLING_TICKETS_FOR_MONSTER_RACE_S1);
msg.addInt(_raceNumber);
final SystemMessage msg2 = new SystemMessage(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S);
msg2.addInt(10);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg, msg2);
break;
}
case 600: // 10 min
{
final SystemMessage msg = new SystemMessage(SystemMessageId.NOW_SELLING_TICKETS_FOR_MONSTER_RACE_S1);
msg.addInt(_raceNumber);
final SystemMessage msg2 = new SystemMessage(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S);
msg2.addInt(5);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg, msg2);
break;
}
case 840: // 14 min
{
final SystemMessage msg = new SystemMessage(SystemMessageId.NOW_SELLING_TICKETS_FOR_MONSTER_RACE_S1);
msg.addInt(_raceNumber);
final SystemMessage msg2 = new SystemMessage(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S);
msg2.addInt(1);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg, msg2);
break;
}
case 900: // 15 min
{
_state = RaceState.WAITING;
calculateOdds();
final SystemMessage msg = new SystemMessage(SystemMessageId.TICKETS_ARE_NOW_AVAILABLE_FOR_MONSTER_RACE_S1);
msg.addInt(_raceNumber);
final SystemMessage msg2 = new SystemMessage(SystemMessageId.TICKETS_SALES_ARE_CLOSED_FOR_MONSTER_RACE_S1_ODDS_ARE_POSTED);
msg2.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg, msg2);
break;
}
case 960: // 16 min
case 1020: // 17 min
{
final int minutes = (_finalCountdown == 960) ? 2 : 1;
final SystemMessage msg = new SystemMessage(SystemMessageId.MONSTER_RACE_S2_WILL_BEGIN_IN_S1_MINUTE_S);
msg.addInt(minutes);
msg.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg);
break;
}
case 1050: // 17 min 30 sec
{
final SystemMessage msg = new SystemMessage(SystemMessageId.MONSTER_RACE_S1_WILL_BEGIN_IN_30_SECONDS);
msg.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg);
break;
}
case 1070: // 17 min 50 sec
{
final SystemMessage msg = new SystemMessage(SystemMessageId.MONSTER_RACE_S1_IS_ABOUT_TO_BEGIN_COUNTDOWN_IN_FIVE_SECONDS);
msg.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg);
break;
}
case 1075: // 17 min 55 sec
case 1076: // 17 min 56 sec
case 1077: // 17 min 57 sec
case 1078: // 17 min 58 sec
case 1079: // 17 min 59 sec
{
final int seconds = 1080 - _finalCountdown;
final SystemMessage msg = new SystemMessage(SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SECOND_S);
msg.addInt(seconds);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg);
break;
}
case 1080: // 18 min
{
_state = RaceState.STARTING_RACE;
_packet = new MonRaceInfo(CODES[1][0], CODES[1][1], getMonsters(), getSpeeds());
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, new SystemMessage(SystemMessageId.THEY_RE_OFF), SOUND_1, SOUND_2, _packet);
break;
}
case 1085: // 18 min 5 sec
{
_packet = new MonRaceInfo(CODES[2][0], CODES[2][1], getMonsters(), getSpeeds());
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, _packet);
break;
}
case 1115: // 18 min 35 sec
{
_state = RaceState.RACE_END;
// Populate history info with data, stores it in database.
final HistoryInfo info = _history.get(_history.size() - 1);
info.setFirst(getFirstPlace());
info.setSecond(getSecondPlace());
info.setOddRate(_odds.get(getFirstPlace() - 1));
saveHistory(info);
clearBets();
final SystemMessage msg = new SystemMessage(SystemMessageId.FIRST_PRIZE_GOES_TO_THE_PLAYER_IN_LANE_S1_SECOND_PRIZE_GOES_TO_THE_PLAYER_IN_LANE_S2);
msg.addInt(getFirstPlace());
msg.addInt(getSecondPlace());
final SystemMessage msg2 = new SystemMessage(SystemMessageId.MONSTER_RACE_S1_IS_FINISHED);
msg2.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg, msg2);
_raceNumber++;
break;
}
case 1140: // 19 min
{
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, new DeleteObject(getMonsters()[0]), new DeleteObject(getMonsters()[1]), new DeleteObject(getMonsters()[2]), new DeleteObject(getMonsters()[3]), new DeleteObject(getMonsters()[4]), new DeleteObject(getMonsters()[5]), new DeleteObject(getMonsters()[6]), new DeleteObject(getMonsters()[7]));
break;
}
}
_finalCountdown += 1;
}
}
public void newRace()
{
// Edit _history.
_history.add(new HistoryInfo(_raceNumber, 0, 0, 0));
// Randomize _npcTemplates.
Collections.shuffle(_npcTemplates);
// Setup 8 new creatures ; pickup the first 8 from _npcTemplates.
for (int i = 0; i < 8; i++)
{
try
{
final NpcTemplate template = NpcData.getInstance().getTemplate(_npcTemplates.get(i));
_monsters[i] = (Npc) Class.forName("org.l2jmobius.gameserver.model.actor.instance." + template.getType() + "Instance").getConstructors()[0].newInstance(template);
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "", e);
}
}
}
public void newSpeeds()
{
_speeds = new int[8][20];
int total = 0;
_first[1] = 0;
_second[1] = 0;
for (int i = 0; i < 8; i++)
{
total = 0;
for (int j = 0; j < 20; j++)
{
if (j == 19)
{
_speeds[i][j] = 100;
}
else
{
_speeds[i][j] = Rnd.get(60) + 65;
}
total += _speeds[i][j];
}
if (total >= _first[1])
{
_second[0] = _first[0];
_second[1] = _first[1];
_first[0] = 8 - i;
_first[1] = total;
}
else if (total >= _second[1])
{
_second[0] = 8 - i;
_second[1] = total;
}
}
}
/**
* Load past races informations, feeding _history arrayList.<br>
* Also sets _raceNumber, based on latest HistoryInfo loaded.
*/
protected void loadHistory()
{
try (Connection con = DatabaseFactory.getConnection())
{
PreparedStatement statement = con.prepareStatement("SELECT * FROM mdt_history");
ResultSet rset = statement.executeQuery();
while (rset.next())
{
_history.add(new HistoryInfo(rset.getInt("race_id"), rset.getInt("first"), rset.getInt("second"), rset.getDouble("odd_rate")));
_raceNumber++;
}
rset.close();
statement.close();
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, "MonsterRace: Can't load history: " + e.getMessage(), e);
}
LOGGER.info("MonsterRace: loaded " + _history.size() + " records, currently on race #" + _raceNumber);
}
/**
* Save an history record into database.
* @param history The infos to store.
*/
protected void saveHistory(HistoryInfo history)
{
try (Connection con = DatabaseFactory.getConnection())
{
PreparedStatement statement = con.prepareStatement("INSERT INTO mdt_history (race_id, first, second, odd_rate) VALUES (?,?,?,?)");
statement.setInt(1, history.getRaceId());
statement.setInt(2, history.getFirst());
statement.setInt(3, history.getSecond());
statement.setDouble(4, history.getOddRate());
statement.execute();
statement.close();
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, "MonsterRace: Can't save history: " + e.getMessage(), e);
}
}
/**
* Load current bets per lane ; initialize the map keys.
*/
protected void loadBets()
{
try (Connection con = DatabaseFactory.getConnection())
{
PreparedStatement statement = con.prepareStatement("SELECT * FROM mdt_bets");
ResultSet rset = statement.executeQuery();
while (rset.next())
{
setBetOnLane(rset.getInt("lane_id"), rset.getLong("bet"), false);
}
rset.close();
statement.close();
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, "MonsterRace: Can't load bets: " + e.getMessage(), e);
}
}
/**
* Save the current lane bet into database.
* @param lane : The lane to affect.
* @param sum : The sum to set.
*/
protected void saveBet(int lane, long sum)
{
try (Connection con = DatabaseFactory.getConnection())
{
PreparedStatement statement = con.prepareStatement("REPLACE INTO mdt_bets (lane_id, bet) VALUES (?,?)");
statement.setInt(1, lane);
statement.setLong(2, sum);
statement.execute();
statement.close();
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, "MonsterRace: Can't save bet: " + e.getMessage(), e);
}
}
/**
* Clear all lanes bets, either on database or Map.
*/
protected void clearBets()
{
for (int key : _betsPerLane.keySet())
{
_betsPerLane.put(key, 0L);
}
try (Connection con = DatabaseFactory.getConnection())
{
PreparedStatement statement = con.prepareStatement("UPDATE mdt_bets SET bet = 0");
statement.execute();
statement.close();
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, "MonsterRace: Can't clear bets: " + e.getMessage(), e);
}
}
/**
* Setup lane bet, based on previous value (if any).
* @param lane : The lane to edit.
* @param amount : The amount to add.
* @param saveOnDb : Should it be saved on db or not.
*/
public void setBetOnLane(int lane, long amount, boolean saveOnDb)
{
final long sum = (_betsPerLane.containsKey(lane)) ? _betsPerLane.get(lane) + amount : amount;
_betsPerLane.put(lane, sum);
if (saveOnDb)
{
saveBet(lane, sum);
}
}
/**
* Calculate odds for every lane, based on others lanes.
*/
protected void calculateOdds()
{
// Clear previous List holding old odds.
_odds.clear();
// Sort bets lanes per lane.
final Map<Integer, Long> sortedLanes = new TreeMap<>(_betsPerLane);
// Pass a first loop in order to calculate total sum of all lanes.
long sumOfAllLanes = 0;
for (long amount : sortedLanes.values())
{
sumOfAllLanes += amount;
}
// As we get the sum, we can now calculate the odd rate of each lane.
for (long amount : sortedLanes.values())
{
_odds.add((amount == 0) ? 0D : Math.max(1.25, (sumOfAllLanes * 0.7) / amount));
}
}
public Npc[] getMonsters()
{
return _monsters;
}
public int[][] getSpeeds()
{
return _speeds;
}
public int getFirstPlace()
{
return _first[0];
}
public int getSecondPlace()
{
return _second[0];
}
public MonRaceInfo getRacePacket()
{
return _packet;
}
public RaceState getCurrentRaceState()
{
return _state;
}
public int getRaceNumber()
{
return _raceNumber;
}
public List<HistoryInfo> getHistory()
{
return _history;
}
public List<Double> getOdds()
{
return _odds;
}
public static MonsterRace getInstance()
{
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder
{
protected static final MonsterRace INSTANCE = new MonsterRace();
}
}

View File

@@ -16,69 +16,26 @@
*/ */
package org.l2jmobius.gameserver.model.actor.instance; package org.l2jmobius.gameserver.model.actor.instance;
import java.util.Collection; import java.util.List;
import java.util.concurrent.ConcurrentHashMap; import java.util.Locale;
import org.l2jmobius.Config; import org.l2jmobius.Config;
import org.l2jmobius.commons.concurrent.ThreadPool; import org.l2jmobius.commons.util.StringUtil;
import org.l2jmobius.gameserver.MonsterRace;
import org.l2jmobius.gameserver.enums.InstanceType;
import org.l2jmobius.gameserver.idfactory.IdFactory; import org.l2jmobius.gameserver.idfactory.IdFactory;
import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.instancemanager.games.MonsterRace;
import org.l2jmobius.gameserver.instancemanager.games.MonsterRace.HistoryInfo;
import org.l2jmobius.gameserver.instancemanager.games.MonsterRace.RaceState;
import org.l2jmobius.gameserver.model.actor.Npc; import org.l2jmobius.gameserver.model.actor.Npc;
import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate; import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate;
import org.l2jmobius.gameserver.model.itemcontainer.Inventory;
import org.l2jmobius.gameserver.model.items.instance.ItemInstance; import org.l2jmobius.gameserver.model.items.instance.ItemInstance;
import org.l2jmobius.gameserver.network.SystemMessageId; import org.l2jmobius.gameserver.network.SystemMessageId;
import org.l2jmobius.gameserver.network.serverpackets.ActionFailed; import org.l2jmobius.gameserver.network.serverpackets.ActionFailed;
import org.l2jmobius.gameserver.network.serverpackets.DeleteObject;
import org.l2jmobius.gameserver.network.serverpackets.IClientOutgoingPacket;
import org.l2jmobius.gameserver.network.serverpackets.InventoryUpdate;
import org.l2jmobius.gameserver.network.serverpackets.MonRaceInfo;
import org.l2jmobius.gameserver.network.serverpackets.NpcHtmlMessage; import org.l2jmobius.gameserver.network.serverpackets.NpcHtmlMessage;
import org.l2jmobius.gameserver.network.serverpackets.PlaySound;
import org.l2jmobius.gameserver.network.serverpackets.SystemMessage; import org.l2jmobius.gameserver.network.serverpackets.SystemMessage;
import org.l2jmobius.gameserver.util.Broadcast;
public class RaceManagerInstance extends Npc public class RaceManagerInstance extends Npc
{ {
public static final int LANES = 8; protected static final int TICKET_PRICES[] =
public static final int WINDOW_START = 0;
private static Collection<RaceManagerInstance> _managers;
protected static int _raceNumber = 4;
// Time Constants
private static final long SECOND = 1000;
private static final long MINUTE = 60 * SECOND;
private static int _minutes = 5;
// States
private static final int ACCEPTING_BETS = 0;
private static final int WAITING = 1;
private static final int STARTING_RACE = 2;
private static final int RACE_END = 3;
private static int _state = RACE_END;
protected static final int[][] _codes =
{
{
-1,
0
},
{
0,
15322
},
{
13765,
-1
}
};
private static boolean _notInitialized = true;
protected static MonRaceInfo _packet;
protected static final int[] _cost =
{ {
100, 100,
500, 500,
@@ -93,223 +50,167 @@ public class RaceManagerInstance extends Npc
public RaceManagerInstance(NpcTemplate template) public RaceManagerInstance(NpcTemplate template)
{ {
super(template); super(template);
setInstanceType(InstanceType.RaceManagerInstance);
if (!Config.ALLOW_RACE)
{
return;
}
if (_notInitialized)
{
_notInitialized = false;
_managers = ConcurrentHashMap.newKeySet();
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKETS_ARE_NOW_AVAILABLE_FOR_MONSTER_RACE_S1), 0, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.NOW_SELLING_TICKETS_FOR_MONSTER_RACE_S1), 30 * SECOND, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKETS_ARE_NOW_AVAILABLE_FOR_MONSTER_RACE_S1), MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.NOW_SELLING_TICKETS_FOR_MONSTER_RACE_S1), MINUTE + (30 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S), 2 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S), 3 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S), 4 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S), 5 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S), 6 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKETS_SALES_ARE_CLOSED_FOR_MONSTER_RACE_S1_ODDS_ARE_POSTED), 7 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.MONSTER_RACE_S2_WILL_BEGIN_IN_S1_MINUTE_S), 7 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.MONSTER_RACE_S2_WILL_BEGIN_IN_S1_MINUTE_S), 8 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.MONSTER_RACE_S1_WILL_BEGIN_IN_30_SECONDS), (8 * MINUTE) + (30 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.MONSTER_RACE_S1_IS_ABOUT_TO_BEGIN_COUNTDOWN_IN_FIVE_SECONDS), (8 * MINUTE) + (50 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SECOND_S), (8 * MINUTE) + (55 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SECOND_S), (8 * MINUTE) + (56 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SECOND_S), (8 * MINUTE) + (57 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SECOND_S), (8 * MINUTE) + (58 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SECOND_S), (8 * MINUTE) + (59 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.THEY_RE_OFF), 9 * MINUTE, 10 * MINUTE);
}
_managers.add(this);
}
@Override
public boolean isAutoAttackable(Creature attacker)
{
if (attacker.isMonster())
{
return true;
}
return super.isAutoAttackable(attacker);
}
class Announcement implements Runnable
{
private final SystemMessageId _type;
public Announcement(SystemMessageId pType)
{
_type = pType;
}
@Override
public void run()
{
makeAnnouncement(_type);
}
}
public void makeAnnouncement(SystemMessageId type)
{
final SystemMessage sm = new SystemMessage(type);
switch (type.getId())
{
case 816: // SystemMessageId.TICKETS_ARE_NOW_AVAILABLE_FOR_MONSTER_RACE_S1
case 817: // SystemMessageId.NOW_SELLING_TICKETS_FOR_MONSTER_RACE_S1
{
if (_state != ACCEPTING_BETS)
{// LOGGER.info("Race Initializing");
_state = ACCEPTING_BETS;
startRace();
} // else{LOGGER.info("Race open");}
sm.addInt(_raceNumber);
break;
}
case 818: // SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S
case 820: // SystemMessageId.MONSTER_RACE_S2_WILL_BEGIN_IN_S1_MINUTE_S
case 823: // SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SECOND_S
{
sm.addInt(_minutes);
if (type.getId() == 820)
{
sm.addInt(_raceNumber);
}
_minutes--;
break;
}
case 819: // SystemMessageId.TICKETS_SALES_ARE_CLOSED_FOR_MONSTER_RACE_S1_ODDS_ARE_POSTED
{
// LOGGER.info("Sales closed");
sm.addInt(_raceNumber);
_state = WAITING;
_minutes = 2;
break;
}
case 821: // SystemMessageId.MONSTER_RACE_S1_WILL_BEGIN_IN_30_SECONDS
case 822: // SystemMessageId.MONSTER_RACE_S1_IS_ABOUT_TO_BEGIN_COUNTDOWN_IN_FIVE_SECONDS
case 825: // SystemMessageId.MONSTER_RACE_S1_IS_FINISHED
{
sm.addInt(_raceNumber);
_minutes = 5;
break;
}
case 826: // SystemMessageId.FIRST_PRIZE_GOES_TO_THE_PLAYER_IN_LANE_S1_SECOND_PRIZE_GOES_TO_THE_PLAYER_IN_LANE_S2
{
// LOGGER.info("Placing");
_state = RACE_END;
sm.addInt(MonsterRace.getInstance().getFirstPlace());
sm.addInt(MonsterRace.getInstance().getSecondPlace());
break;
}
}
// _logn.info("Counter: "+minutes);
// LOGGER.info("State: "+state);
broadcast(sm);
// LOGGER.info("Player's known: "+getKnownPlayers().size());
if (type == SystemMessageId.THEY_RE_OFF)
{
// LOGGER.info("Starting race");
_state = STARTING_RACE;
startRace();
_minutes = 5;
}
}
protected void broadcast(IClientOutgoingPacket pkt)
{
for (RaceManagerInstance manager : _managers)
{
if (!manager.isDead())
{
Broadcast.toKnownPlayers(manager, pkt);
}
}
}
public void sendMonsterInfo()
{
broadcast(_packet);
}
private void startRace()
{
final MonsterRace race = MonsterRace.getInstance();
if (_state == STARTING_RACE)
{
// state++;
broadcast(new PlaySound(1, "S_Race", 0, 0, 0, 0, 0));
broadcast(new PlaySound(0, "ItemSound2.race_start", 1, 121209259, 12125, 182487, -3559));
_packet = new MonRaceInfo(_codes[1][0], _codes[1][1], race.getMonsters(), race.getSpeeds());
sendMonsterInfo();
ThreadPool.schedule(new RunRace(), 5000);
}
else
{
// state++;
race.newRace();
race.newSpeeds();
_packet = new MonRaceInfo(_codes[0][0], _codes[0][1], race.getMonsters(), race.getSpeeds());
sendMonsterInfo();
}
} }
@Override @Override
public void onBypassFeedback(PlayerInstance player, String command) public void onBypassFeedback(PlayerInstance player, String command)
{ {
if (command.startsWith("BuyTicket") && (_state != ACCEPTING_BETS))
{
if (!Config.ALLOW_RACE)
{
return;
}
player.sendPacket(SystemMessageId.MONSTER_RACE_TICKETS_ARE_NO_LONGER_AVAILABLE);
command = "Chat 0";
}
if (command.startsWith("ShowOdds") && (_state == ACCEPTING_BETS))
{
if (!Config.ALLOW_RACE)
{
return;
}
player.sendPacket(SystemMessageId.MONSTER_RACE_PAYOUT_INFORMATION_IS_NOT_AVAILABLE_WHILE_TICKETS_ARE_BEING_SOLD);
command = "Chat 0";
}
if (command.startsWith("BuyTicket")) if (command.startsWith("BuyTicket"))
{ {
if (!Config.ALLOW_RACE) if (!Config.ALLOW_RACE || (MonsterRace.getInstance().getCurrentRaceState() != RaceState.ACCEPTING_BETS))
{ {
player.sendPacket(SystemMessageId.MONSTER_RACE_TICKETS_ARE_NO_LONGER_AVAILABLE);
super.onBypassFeedback(player, "Chat 0");
return; return;
} }
int val = Integer.parseInt(command.substring(10)); int val = Integer.parseInt(command.substring(10));
if (val == 0) if (val == 0)
{ {
player.setRace(0, 0); player.setRace(0, 0);
player.setRace(1, 0); player.setRace(1, 0);
} }
if (((val == 10) && (player.getRace(0) == 0)) || ((val == 20) && (player.getRace(0) == 0) && (player.getRace(1) == 0))) if (((val == 10) && (player.getRace(0) == 0)) || ((val == 20) && (player.getRace(0) == 0) && (player.getRace(1) == 0)))
{ {
val = 0; val = 0;
} }
showBuyTicket(player, val);
} String search, replace;
else if (command.equals("ShowOdds"))
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
if (val < 10)
{ {
if (!Config.ALLOW_RACE) html.setFile(player, getHtmlPath(getId(), 2, player));
for (int i = 0; i < 8; i++)
{
int n = i + 1;
search = "Mob" + n;
html.replace(search, MonsterRace.getInstance().getMonsters()[i].getTemplate().getName());
}
search = "No1";
if (val == 0)
{
html.replace(search, "");
}
else
{
html.replace(search, val);
player.setRace(0, val);
}
}
else if (val < 20)
{
if (player.getRace(0) == 0)
{ {
return; return;
} }
showOdds(player);
html.setFile(player, getHtmlPath(getId(), 3, player));
html.replace("0place", player.getRace(0));
search = "Mob1";
replace = MonsterRace.getInstance().getMonsters()[player.getRace(0) - 1].getTemplate().getName();
html.replace(search, replace);
search = "0adena";
if (val == 10)
{
html.replace(search, "");
}
else
{
html.replace(search, TICKET_PRICES[val - 11]);
player.setRace(1, val - 10);
}
}
else if (val == 20)
{
if ((player.getRace(0) == 0) || (player.getRace(1) == 0))
{
return;
}
html.setFile(player, getHtmlPath(getId(), 4, player));
html.replace("0place", player.getRace(0));
search = "Mob1";
replace = MonsterRace.getInstance().getMonsters()[player.getRace(0) - 1].getTemplate().getName();
html.replace(search, replace);
search = "0adena";
int price = TICKET_PRICES[player.getRace(1) - 1];
html.replace(search, price);
search = "0tax";
int tax = 0;
html.replace(search, tax);
search = "0total";
int total = price + tax;
html.replace(search, total);
}
else
{
if ((player.getRace(0) == 0) || (player.getRace(1) == 0))
{
return;
}
int ticket = player.getRace(0);
int priceId = player.getRace(1);
if (!player.reduceAdena("Race", TICKET_PRICES[priceId - 1], this, true))
{
return;
}
player.setRace(0, 0);
player.setRace(1, 0);
ItemInstance item = new ItemInstance(IdFactory.getInstance().getNextId(), 4443);
item.setCount(1);
item.setEnchantLevel(MonsterRace.getInstance().getRaceNumber());
item.setCustomType1(ticket);
item.setCustomType2(TICKET_PRICES[priceId - 1] / 100);
player.addItem("Race", item, player, false);
final SystemMessage msg = new SystemMessage(SystemMessageId.ACQUIRED_S1_S2);
msg.addInt(MonsterRace.getInstance().getRaceNumber());
msg.addItemName(4443);
player.sendPacket(msg);
// Refresh lane bet.
MonsterRace.getInstance().setBetOnLane(ticket, TICKET_PRICES[priceId - 1], true);
super.onBypassFeedback(player, "Chat 0");
return;
}
html.replace("1race", MonsterRace.getInstance().getRaceNumber());
html.replace("%objectId%", getObjectId());
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
else if (command.equals("ShowOdds"))
{
if (!Config.ALLOW_RACE || (MonsterRace.getInstance().getCurrentRaceState() == RaceState.ACCEPTING_BETS))
{
player.sendPacket(SystemMessageId.MONSTER_RACE_PAYOUT_INFORMATION_IS_NOT_AVAILABLE_WHILE_TICKETS_ARE_BEING_SOLD);
super.onBypassFeedback(player, "Chat 0");
return;
}
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
html.setFile(player, getHtmlPath(getId(), 5, player));
for (int i = 0; i < 8; i++)
{
final int n = i + 1;
html.replace("Mob" + n, MonsterRace.getInstance().getMonsters()[i].getTemplate().getName());
// Odd
final double odd = MonsterRace.getInstance().getOdds().get(i);
html.replace("Odd" + n, (odd > 0D) ? String.format(Locale.ENGLISH, "%.1f", odd) : "&$804;");
}
html.replace("1race", MonsterRace.getInstance().getRaceNumber());
html.replace("%objectId%", getObjectId());
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
} }
else if (command.equals("ShowInfo")) else if (command.equals("ShowInfo"))
{ {
@@ -317,269 +218,160 @@ public class RaceManagerInstance extends Npc
{ {
return; return;
} }
showMonsterInfo(player);
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
html.setFile(player, getHtmlPath(getId(), 6, player));
for (int i = 0; i < 8; i++)
{
int n = i + 1;
String search = "Mob" + n;
html.replace(search, MonsterRace.getInstance().getMonsters()[i].getTemplate().getName());
} }
else if (command.equals("calculateWin")) html.replace("%objectId%", getObjectId());
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
else if (command.equals("ShowTickets"))
{ {
if (!Config.ALLOW_RACE) if (!Config.ALLOW_RACE)
{ {
super.onBypassFeedback(player, "Chat 0");
return; return;
} }
// displayCalculateWinnings(player);
// Generate data.
final StringBuilder sb = new StringBuilder();
// Retrieve player's tickets.
for (ItemInstance ticket : player.getInventory().getAllItemsByItemId(4443))
{
// Don't list current race tickets.
if (ticket.getEnchantLevel() == MonsterRace.getInstance().getRaceNumber())
{
continue;
} }
else if (command.equals("viewHistory"))
StringUtil.append(sb, "<tr><td><a action=\"bypass -h npc_%objectId%_ShowTicket ", "" + ticket.getObjectId(), "\">", "" + ticket.getEnchantLevel(), " Race Number</a></td><td align=right><font color=\"LEVEL\">", "" + ticket.getCustomType1(), "</font> Number</td><td align=right><font color=\"LEVEL\">", "" + (ticket.getCustomType2() * 100), "</font> Adena</td></tr>");
}
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
html.setFile(player, getHtmlPath(getId(), 7, player));
html.replace("%tickets%", sb.toString());
html.replace("%objectId%", getObjectId());
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
else if (command.startsWith("ShowTicket"))
{
// Retrieve ticket objectId.
final int val = Integer.parseInt(command.substring(11));
if (!Config.ALLOW_RACE || (val == 0))
{
super.onBypassFeedback(player, "Chat 0");
return;
}
// Retrieve ticket on player's inventory.
final ItemInstance ticket = player.getInventory().getItemByObjectId(val);
if (ticket == null)
{
super.onBypassFeedback(player, "Chat 0");
return;
}
final int raceId = ticket.getEnchantLevel();
final int lane = ticket.getCustomType1();
final int bet = ticket.getCustomType2() * 100;
// Retrieve HistoryInfo for that race.
final HistoryInfo info = MonsterRace.getInstance().getHistory().get(raceId - 1);
if (info == null)
{
super.onBypassFeedback(player, "Chat 0");
return;
}
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
html.setFile(player, getHtmlPath(getId(), 8, player));
html.replace("%raceId%", raceId);
html.replace("%lane%", lane);
html.replace("%bet%", bet);
html.replace("%firstLane%", info.getFirst());
html.replace("%odd%", (lane == info.getFirst()) ? String.format(Locale.ENGLISH, "%.2f", info.getOddRate()) : "0.01");
html.replace("%objectId%", getObjectId());
html.replace("%ticketObjectId%", val);
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
else if (command.startsWith("CalculateWin"))
{
// Retrieve ticket objectId.
final int val = Integer.parseInt(command.substring(13));
if (!Config.ALLOW_RACE || (val == 0))
{
super.onBypassFeedback(player, "Chat 0");
return;
}
// Delete ticket on player's inventory.
final ItemInstance ticket = player.getInventory().getItemByObjectId(val);
if (ticket == null)
{
super.onBypassFeedback(player, "Chat 0");
return;
}
final int raceId = ticket.getEnchantLevel();
final int lane = ticket.getCustomType1();
final int bet = ticket.getCustomType2() * 100;
// Retrieve HistoryInfo for that race.
final HistoryInfo info = MonsterRace.getInstance().getHistory().get(raceId - 1);
if (info == null)
{
super.onBypassFeedback(player, "Chat 0");
return;
}
// Destroy the ticket.
if (player.destroyItem("MonsterTrack", ticket, this, true))
{
player.addAdena("MonsterTrack", (int) (bet * ((lane == info.getFirst()) ? info.getOddRate() : 0.01)), this, true);
}
super.onBypassFeedback(player, "Chat 0");
return;
}
else if (command.equals("ViewHistory"))
{ {
if (!Config.ALLOW_RACE) if (!Config.ALLOW_RACE)
{ {
super.onBypassFeedback(player, "Chat 0");
return; return;
} }
// displayHistory(player);
// Generate data.
final StringBuilder sb = new StringBuilder();
// Use whole history, pickup from 'last element' and stop at 'latest element - 7'.
final List<HistoryInfo> history = MonsterRace.getInstance().getHistory();
for (int i = history.size() - 1; i >= Math.max(0, history.size() - 7); i--)
{
final HistoryInfo info = history.get(i);
StringUtil.append(sb, "<tr><td><font color=\"LEVEL\">", "" + info.getRaceId(), "</font> th</td><td><font color=\"LEVEL\">", "" + info.getFirst(), "</font> Lane </td><td><font color=\"LEVEL\">", "" + info.getSecond(), "</font> Lane</td><td align=right><font color=00ffff>", String.format(Locale.ENGLISH, "%.2f", info.getOddRate()), "</font> Times</td></tr>");
}
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
html.setFile(player, getHtmlPath(getId(), 9, player));
html.replace("%infos%", sb.toString());
html.replace("%objectId%", getObjectId());
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
} }
else else
{ {
// getKnownList().removeKnownObject(player);
super.onBypassFeedback(player, command); super.onBypassFeedback(player, command);
} }
} }
public void showOdds(PlayerInstance player)
{
if (_state == ACCEPTING_BETS)
{
return;
}
final int npcId = getTemplate().getId();
String filename;
String search;
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
filename = getHtmlPath(npcId, 5, player);
html.setFile(player, filename);
for (int i = 0; i < 8; i++)
{
final int n = i + 1;
search = "Mob" + n;
html.replace(search, MonsterRace.getInstance().getMonsters()[i].getTemplate().getName());
}
html.replace("1race", String.valueOf(_raceNumber));
html.replace("%objectId%", String.valueOf(getObjectId()));
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
public void showMonsterInfo(PlayerInstance player)
{
final int npcId = getTemplate().getId();
String filename;
String search;
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
filename = getHtmlPath(npcId, 6, player);
html.setFile(player, filename);
for (int i = 0; i < 8; i++)
{
final int n = i + 1;
search = "Mob" + n;
html.replace(search, MonsterRace.getInstance().getMonsters()[i].getTemplate().getName());
}
html.replace("%objectId%", String.valueOf(getObjectId()));
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
public void showBuyTicket(PlayerInstance player, int value)
{
if (_state != ACCEPTING_BETS)
{
return;
}
final int npcId = getTemplate().getId();
SystemMessage sm;
String filename;
String search;
String replace;
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
if (value < 10)
{
filename = getHtmlPath(npcId, 2, player);
html.setFile(player, filename);
for (int i = 0; i < 8; i++)
{
final int n = i + 1;
search = "Mob" + n;
html.replace(search, MonsterRace.getInstance().getMonsters()[i].getTemplate().getName());
}
search = "No1";
if (value == 0)
{
html.replace(search, "");
}
else
{
html.replace(search, Integer.toString(value));
player.setRace(0, value);
}
}
else if (value < 20)
{
if (player.getRace(0) == 0)
{
return;
}
filename = getHtmlPath(npcId, 3, player);
html.setFile(player, filename);
html.replace("0place", Integer.toString(player.getRace(0)));
search = "Mob1";
replace = MonsterRace.getInstance().getMonsters()[player.getRace(0) - 1].getTemplate().getName();
html.replace(search, replace);
search = "0adena";
if (value == 10)
{
html.replace(search, "");
}
else
{
html.replace(search, Integer.toString(_cost[value - 11]));
player.setRace(1, value - 10);
}
}
else if (value == 20)
{
if ((player.getRace(0) == 0) || (player.getRace(1) == 0))
{
return;
}
filename = getHtmlPath(npcId, 4, player);
html.setFile(player, filename);
html.replace("0place", Integer.toString(player.getRace(0)));
search = "Mob1";
replace = MonsterRace.getInstance().getMonsters()[player.getRace(0) - 1].getTemplate().getName();
html.replace(search, replace);
search = "0adena";
final int price = _cost[player.getRace(1) - 1];
html.replace(search, Integer.toString(price));
search = "0tax";
final int tax = 0;
html.replace(search, Integer.toString(tax));
search = "0total";
final int total = price + tax;
html.replace(search, Integer.toString(total));
}
else
{
if ((player.getRace(0) == 0) || (player.getRace(1) == 0))
{
return;
}
final int ticket = player.getRace(0);
final int priceId = player.getRace(1);
if (!player.reduceAdena("Race", _cost[priceId - 1], this, true))
{
return;
}
player.setRace(0, 0);
player.setRace(1, 0);
sm = new SystemMessage(SystemMessageId.ACQUIRED_S1_S2);
sm.addInt(_raceNumber);
sm.addItemName(4443);
player.sendPacket(sm);
final ItemInstance item = new ItemInstance(IdFactory.getInstance().getNextId(), 4443);
item.setCount(1);
item.setEnchantLevel(_raceNumber);
item.setCustomType1(ticket);
item.setCustomType2(_cost[priceId - 1] / 100);
player.getInventory().addItem("Race", item, player, this);
final InventoryUpdate iu = new InventoryUpdate();
iu.addItem(item);
final ItemInstance adenaupdate = player.getInventory().getItemByItemId(Inventory.ADENA_ID);
iu.addModifiedItem(adenaupdate);
player.sendInventoryUpdate(iu);
return;
}
html.replace("1race", String.valueOf(_raceNumber));
html.replace("%objectId%", String.valueOf(getObjectId()));
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
public static class Race
{
private final Info[] _info;
public Race(Info[] pInfo)
{
_info = pInfo;
}
public Info getLaneInfo(int lane)
{
return _info[lane];
}
public class Info
{
private final int _id;
private final int _place;
private final int _odds;
private final int _payout;
public Info(int pId, int pPlace, int pOdds, int pPayout)
{
_id = pId;
_place = pPlace;
_odds = pOdds;
_payout = pPayout;
}
public int getId()
{
return _id;
}
public int getOdds()
{
return _odds;
}
public int getPayout()
{
return _payout;
}
public int getPlace()
{
return _place;
}
}
}
class RunRace implements Runnable
{
@Override
public void run()
{
_packet = new MonRaceInfo(_codes[2][0], _codes[2][1], MonsterRace.getInstance().getMonsters(), MonsterRace.getInstance().getSpeeds());
sendMonsterInfo();
ThreadPool.schedule(new RunEnd(), 30000);
}
}
class RunEnd implements Runnable
{
@Override
public void run()
{
makeAnnouncement(SystemMessageId.FIRST_PRIZE_GOES_TO_THE_PLAYER_IN_LANE_S1_SECOND_PRIZE_GOES_TO_THE_PLAYER_IN_LANE_S2);
makeAnnouncement(SystemMessageId.MONSTER_RACE_S1_IS_FINISHED);
_raceNumber++;
DeleteObject obj = null;
for (int i = 0; i < 8; i++)
{
obj = new DeleteObject(MonsterRace.getInstance().getMonsters()[i]);
broadcast(obj);
MonsterRace.getInstance().getMonsters()[i].deleteMe();
}
}
}
} }

View File

@@ -20,10 +20,12 @@ import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.l2jmobius.gameserver.enums.ChatType; import org.l2jmobius.gameserver.enums.ChatType;
import org.l2jmobius.gameserver.instancemanager.ZoneManager;
import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.World;
import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.actor.Creature;
import org.l2jmobius.gameserver.model.actor.Summon; import org.l2jmobius.gameserver.model.actor.Summon;
import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
import org.l2jmobius.gameserver.model.zone.ZoneType;
import org.l2jmobius.gameserver.network.serverpackets.CharInfo; import org.l2jmobius.gameserver.network.serverpackets.CharInfo;
import org.l2jmobius.gameserver.network.serverpackets.CreatureSay; import org.l2jmobius.gameserver.network.serverpackets.CreatureSay;
import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage; import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
@@ -191,4 +193,29 @@ public class Broadcast
{ {
toAllOnlinePlayers(new ExShowScreenMessage(text, 10000)); toAllOnlinePlayers(new ExShowScreenMessage(text, 10000));
} }
/**
* Send a packet to all players in a specific zone type.
* @param <T> ZoneType.
* @param zoneType : The zone type to send packets.
* @param packets : The packets to send.
*/
public static <T extends ZoneType> void toAllPlayersInZoneType(Class<T> zoneType, IClientOutgoingPacket... packets)
{
for (ZoneType zone : ZoneManager.getInstance().getAllZones(zoneType))
{
for (Creature creature : zone.getCharactersInside())
{
if (creature == null)
{
continue;
}
for (IClientOutgoingPacket packet : packets)
{
creature.sendPacket(packet);
}
}
}
}
} }

View File

@@ -0,0 +1,15 @@
CREATE TABLE IF NOT EXISTS `mdt_bets` (
`lane_id` INT(1) DEFAULT 0,
`bet` INT DEFAULT 0,
PRIMARY KEY (`lane_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;
INSERT INTO `mdt_bets` VALUES
('1','0'),
('2','0'),
('3','0'),
('4','0'),
('5','0'),
('6','0'),
('7','0'),
('8','0');

View File

@@ -0,0 +1,7 @@
CREATE TABLE IF NOT EXISTS `mdt_history` (
`race_id` MEDIUMINT DEFAULT 0,
`first` INT(1) DEFAULT 0,
`second` INT(1) DEFAULT 0,
`odd_rate` DOUBLE(10,2) DEFAULT 0,
PRIMARY KEY (`race_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;

View File

@@ -416,9 +416,6 @@
<admin command="admin_mobgroup_attackgrp" accessLevel="100" /> <admin command="admin_mobgroup_attackgrp" accessLevel="100" />
<admin command="admin_mobgroup_invul" accessLevel="100" /> <admin command="admin_mobgroup_invul" accessLevel="100" />
<!-- ADMIN MONSTER RACE -->
<admin command="admin_mons" accessLevel="100" />
<!-- ADMIN ONLINE --> <!-- ADMIN ONLINE -->
<admin command="admin_online" accessLevel="100" /> <admin command="admin_online" accessLevel="100" />

View File

@@ -1,19 +1,22 @@
<html><body><br><br> <html><body>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 0">Back</Button><br> <br>
In a monster race eight monsters race at the same time. If you guess which monster is expected to finish first and buy that monster's race ticket, based upon the odds to win you will receive an appropriate amount of Adena.<br> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 0">Go Back</Button>
<font color="LEVEL">How the Race Works</font>Monster races run every 20 minutes in real time.<br> <br>
<font color="LEVEL">How to Participate</font><br> In a monster race eight monsters race at the same time. If you guess which monster is expected to finish first and buy that monster's race ticket, based upon the odds to win you will receive an appropriate amount of adena.<br>
You can purchase a monster race ticket through a race manager NPC. Only between 18 minutes before the start of a race until 3 minutes before the race begins can you buy a ticket for the applicable race. When you purchase a ticket through a race manager, you can find out the names of the participating monsters and their current statistics. Three minutes before a race starts, the race manager will stop ticket sales and announce the monsters' individual odds for the current race.<br> <font color="LEVEL">Racing Schedule</font><br>
<font color="LEVEL">Betting</font><br> Monster races every 20 minutes in real time.<br>
There are two types of race betting. The Win bet means that you back a particular monster to win 1st place. The Place bet means that you will have to guess the 1st and 2nd place monsters, regardless of who finishes first or second.<br> <font color="LEVEL">Method of Race Participation</font><br>
You can purchase a monster race ticket through a race manager NPC. Only between 18 minutes before the start of a race until 3 minutes before the race begins you can buy a ticket for the applicable race. When you purchase a ticket through a conversation with a race manager, you can find out the names of participating monsters in the race and their current statistics. Five minutes before a race starts, the race manager will stop ticket sales and announce monsters' individual odds rate for the current race.<br>
<font color="LEVEL">Types of Race</font><br>
There are two types of race betting. The Win type betting means that you back a monster that will win the first prize. Place type of betting means that you will have to guess the 1st and 2nd winning monsters, regardless of who finishes first or second.<br>
<font color="00FFFF">Win</font><br1> <font color="00FFFF">Win</font><br1>
* Among the entire betting amount for Win betting, after subtracting the race track's own share of profit, the amount left will be your total winnings. Three minutes before the race, each monster's expected odds are announced. Each Win ticket's dividend amount is calculated by multiplying the odds by the price of a ticket. The odds are never less than 1.<br1> * Among the entire betting amount for Win betting, after subtracting the race track's own share of profit, the amount left will be your total winnings. Three minutes before the race each monster's expected odds are announced. Each Win ticket's dividend amount is calculated by multiplying the odds by the price of a ticket. The odds rate is never less than 1.<br1>
* The prize money does not get transferred to the next race.<br> * The prize money does not get transferred to the next race.<br>
<font color="00FFFF">Place</font><br1> <font color="00FFFF">Place</font><br1>
* Place betting is a way of betting when you correctly guess the two monsters who will finish first and second regardless of their order. The method of calculating the dividend amount is the same as Win betting.<br1> * Place betting is a way of betting when you correctly guess the two monsters who will finish first and second regardless of their order. The method of calculating dividend amount is the same as Win betting.<br>
* Currently place betting is not available.<br> * Currently place betting is not offered.<br>
<font color="LEVEL">End of Race</font><br> <font color="LEVEL">End of Race</font><br>
When a race is over, the race manager will shout the results of the race. You can trade your ticket for Adena by speaking to a race manager NPC. Check the Winning Information Data to find out the types of bets and total winnings for the race you bet on, as well as your win status and prize amount. If you won, you can receive your prize money thorough a race manager NPC. If you did not win, a race manager will buy back your ticket for a small amount of money.<br> When a race is over, the race manager will shout out the results of the race. Through a conversation with a race manager NPC, you can trade your ticket for adena. By looking up on the Winning Information Data, you can find out the types of tickets and quantity of the winnings in the race you betted on and your win status and prize amount you won. If you won, you can receive your prize money thorough a race manager NPC. If you did not win, a race manager will buy back your ticket by paying you a very small amount of money.<br>
If a server goes down while tickets are being sold, or while a race is in progress, the race will be cancelled. You can receive a full refund for your ticket for a cancelled race through a race manager.<br> If a server goes down while tickets are being sold, or while a race is in progress, the race will be cancelled. You can receive a full refund for your ticket for a cancelled race through a race manager.<br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 0">Back</Button> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 0">Go Back</Button>
</body></html> </body></html>

View File

@@ -3,6 +3,7 @@
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=1 height=3> <img src="L2UI.SquareBlank" width=1 height=3>
</center> </center>
<table width=270 border=0 bgcolor="111111"> <table width=270 border=0 bgcolor="111111">
<tr> <tr>
<td fixwidth=60>Lane Selection</td> <td fixwidth=60>Lane Selection</td>
@@ -11,6 +12,7 @@
<td fixwidth=30 align=right>Odds</td> <td fixwidth=30 align=right>Odds</td>
</tr> </tr>
</table> </table>
<table width=270 border=0> <table width=270 border=0>
<tr> <tr>
<td fixwidth=13><img src="L2UI.rightBtn2" width=14 height=13></td> <td fixwidth=13><img src="L2UI.rightBtn2" width=14 height=13></td>
@@ -20,18 +22,21 @@
<td fixwidth=30 align=right><font color="00ffff">13%</font></td> <td fixwidth=30 align=right><font color="00ffff">13%</font></td>
</tr> </tr>
</table> </table>
<table width=270 border=0> <table width=270 border=0>
<tr><td width=100>Select Bet Amount</td> <tr><td width=100>Select Bet Amount</td>
<td width=170> <td width=170>
<a action="bypass -h npc_%objectId%_BuyTicket 11" >100 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 12" >500 Adena</a><br1> <a action="bypass -h npc_%objectId%_BuyTicket 11" >100 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 12" >500 Adena</a> <br1>
<a action="bypass -h npc_%objectId%_BuyTicket 13" >1,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 14" >5,000 Adena</a><br1> <a action="bypass -h npc_%objectId%_BuyTicket 13" >1,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 14" >5,000 Adena</a> <br1>
<a action="bypass -h npc_%objectId%_BuyTicket 15" >10,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 16" >20,000 Adena</a><br1> <a action="bypass -h npc_%objectId%_BuyTicket 15" >10,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 16" >20,000 Adena</a> <br1>
<a action="bypass -h npc_%objectId%_BuyTicket 17" >50,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 18" >100,000 Adena</a><br1> <a action="bypass -h npc_%objectId%_BuyTicket 17" >50,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 18" >100,000 Adena</a> <br1>
</td></tr> </td></tr>
</table> </table>
<img src="L2UI.SquareBlank" width=270 height=10> <img src="L2UI.SquareBlank" width=270 height=10>
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=270 height=5> <img src="L2UI.SquareBlank" width=270 height=5>
<table width=270 border=0 cellpadding=0 cellspacing=0> <table width=270 border=0 cellpadding=0 cellspacing=0>
<tr> <tr>
<td width=70 height=20><button value="Select Lane" action="bypass -h npc_%objectId%_BuyTicket 0" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td> <td width=70 height=20><button value="Select Lane" action="bypass -h npc_%objectId%_BuyTicket 0" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td>
@@ -39,4 +44,5 @@
<td width=70 align=right><button value="Confirm" action="bypass -h npc_%objectId%_BuyTicket 20" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td> <td width=70 align=right><button value="Confirm" action="bypass -h npc_%objectId%_BuyTicket 20" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td>
</tr> </tr>
</table> </table>
</body></html> </body></html>

View File

@@ -11,6 +11,7 @@
<td fixwidth=30 align=right>Odds</td> <td fixwidth=30 align=right>Odds</td>
</tr> </tr>
</table> </table>
<table width=270 border=0> <table width=270 border=0>
<tr> <tr>
<td fixwidth=13><img src="L2UI.rightBtn2" width=14 height=13></td> <td fixwidth=13><img src="L2UI.rightBtn2" width=14 height=13></td>
@@ -20,6 +21,7 @@
<td fixwidth=30 align=right><font color="00ffff">13%</font></td> <td fixwidth=30 align=right><font color="00ffff">13%</font></td>
</tr> </tr>
</table> </table>
<table width=270 border=0 bgcolor=111111> <table width=270 border=0 bgcolor=111111>
<tr> <tr>
<td fixwidth=60>Bet Amount </td> <td fixwidth=60>Bet Amount </td>
@@ -37,11 +39,13 @@
<td>Total </td> <td>Total </td>
<td><font color="LEVEL">0total</font> Adena</td> <td><font color="LEVEL">0total</font> Adena</td>
</tr> </tr>
</table><br> </table>
<br>
<center>If the information is correct, please click Confirm.</center><br> <center>If the information is correct, please click Confirm.</center><br>
<img src="L2UI.SquareBlank" width=270 height=10> <img src="L2UI.SquareBlank" width=270 height=10>
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=270 height=5> <img src="L2UI.SquareBlank" width=270 height=5>
<table width=270 border=0 cellpadding=0 cellspacing=0> <table width=270 border=0 cellpadding=0 cellspacing=0>
<tr> <tr>
<td width=70 height=20><button value=&$141; action="bypass -h npc_%objectId%_Chat 0" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td> <td width=70 height=20><button value=&$141; action="bypass -h npc_%objectId%_Chat 0" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td>

View File

@@ -15,49 +15,49 @@
<tr> <tr>
<td fixwidth=40>No.1</td> <td fixwidth=40>No.1</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob1</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob1</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd1</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.2</td> <td fixwidth=40>No.2</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob2</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob2</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd2</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.3</td> <td fixwidth=40>No.3</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob3</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob3</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd3</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.4</td> <td fixwidth=40>No.4</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob4</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob4</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd4</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.5</td> <td fixwidth=40>No.5</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob5</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob5</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd5</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.6</td> <td fixwidth=40>No.6</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob6</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob6</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd6</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.7</td> <td fixwidth=40>No.7</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob7</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob7</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd7</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.8</td> <td fixwidth=40>No.8</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob8</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob8</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd8</font> </td>
</tr> </tr>
</table> </table>
<img src="L2UI.SquareBlank" width=270 height=10> <img src="L2UI.SquareBlank" width=270 height=10>

View File

@@ -3,22 +3,12 @@
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=1 height=3> <img src="L2UI.SquareBlank" width=1 height=3>
<table width=270 border=0 bgcolor=111111> <table width=270 border=0 bgcolor=111111>
<tr><td fixwidth=60>Winnings for a Race.</td><td fixwidth=60 align=right>Lane</td><td fixwidth=160 align=right>Bet Amount</td></tr> <tr><td fixwidth=70>Winnings for a Race.</td><td fixwidth=70 align=right>Lane</td><td fixwidth=130 align=right>Bet Amount</td></tr>
<tr><td fixwidth=60 fixheight=1></td><td fixwidth=60></td><td fixwidth=160></td></tr> <tr><td fixwidth=70 fixheight=1></td><td fixwidth=70></td><td fixwidth=130></td></tr>
</table> </table>
<table width=270 border=0> <table width=270 border=0>
<tr> %tickets%
<td><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 8">1 Race Number</Button></td>
<td align=right><font color="LEVEL">1</font> Number</td>
<td align=right><font color="LEVEL">100</font> Adena</td>
</tr>
</table> </table>
<img src="L2UI.SquareWhite" width=270 height=1>
<table width=270 border=0>
<tr><td width=90><Button ALIGN=LEFT ICON="NORMAL" action="bypass _mrsl01_1">Previous List</Button></td><td width=90 align=center>1 Page </td><td width=90 align=right><Button ALIGN=LEFT ICON="NORMAL" action="bypass _mrsl01_2"> Next List</Button></td></tr>
</table>
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=270 height=5> <img src="L2UI.SquareBlank" width=270 height=5>
<table width=270 border=0 cellpadding=0 cellspacing=0> <table width=270 border=0 cellpadding=0 cellspacing=0>

View File

@@ -4,11 +4,11 @@
<img src="L2UI.SquareBlank" width=1 height=3> <img src="L2UI.SquareBlank" width=1 height=3>
<table width=270 border=0> <table width=270 border=0>
<tr> <tr>
<td width=230>Race #19090<br> <td width=230>Race #%raceId%<br>
Lane 1<br> Lane: %lane%<br>
Bet: 100 adena<br> Bet: %bet% Adena<br>
1st place: Lane [4]<br> 1st place: Lane [%firstLane%]<br>
Odds:[0.01] Odds: [%odd%]
</td> </td>
</tr> </tr>
</table> </table>
@@ -18,8 +18,8 @@ Odds:[0.01]
<table width=270 border=0 cellpadding=0 cellspacing=0> <table width=270 border=0 cellpadding=0 cellspacing=0>
<tr> <tr>
<td width=90 height=20><button value="Previous" action="bypass -h npc_%objectId%_Chat 7" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td> <td width=90 height=20><button value="Previous" action="bypass -h npc_%objectId%_ShowTickets" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td>
<td width=90></td> <td width=90></td>
<td width=90 align=right><button value="Calculate" action="bypass _mrsl03_30635184" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td> <td width=90 align=right><button value="Calculate" action="bypass -h npc_%objectId%_CalculateWin %ticketObjectId%" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td>
</tr></table> </tr></table>
</body></html> </body></html>

View File

@@ -7,41 +7,7 @@
</table> </table>
<table width=270 border=0> <table width=270 border=0>
<tr><td fixwidth=60 fixheight=1></td><td fixwidth=70></td><td fixwidth=70></td><td fixwidth=70></td></tr> <tr><td fixwidth=60 fixheight=1></td><td fixwidth=70></td><td fixwidth=70></td><td fixwidth=70></td></tr>
<tr> %infos%
<td><font color="LEVEL">7</font> th</td>
<td><font color="LEVEL">8</font> Lane </td><td><font color="LEVEL">1</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">6</font> th</td>
<td><font color="LEVEL">4</font> Lane </td><td><font color="LEVEL">8</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">5</font> th</td>
<td><font color="LEVEL">7</font> Lane </td><td><font color="LEVEL">2</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">4</font> th</td>
<td><font color="LEVEL">6</font> Lane </td><td><font color="LEVEL">8</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">3</font> th</td>
<td><font color="LEVEL">4</font> Lane </td><td><font color="LEVEL">1</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">2</font> th</td>
<td><font color="LEVEL">1</font> Lane </td><td><font color="LEVEL">8</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">1</font> th</td>
<td><font color="LEVEL">7</font> Lane </td><td><font color="LEVEL">6</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
</table> </table>
<img src="L2UI.SquareBlank" width=270 height=10> <img src="L2UI.SquareBlank" width=270 height=10>
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>

View File

@@ -1,15 +1,15 @@
<html>
<body>
<center>Monster Race Manager</center> <center>Monster Race Manager</center>
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareBlank" width=1 height=3> <img src="L2UI.SquareWhite" width=270 height=1>
<table width=270 border=0> <img src="L2UI.SquareBlank" width=1 height=3>
<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 1">Monster Race Help</Button></td></tr> <br>
<!--<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_ShowOdds">View Odds</Button></td></tr>--> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 1">Monster Race Help</Button>
<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_ShowInfo">View Monster Information</Button></td></tr> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_ShowOdds">View Odds</Button>
<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_BuyTicket 0">Purchase Ticket</Button></td></tr> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_ShowInfo">View Monster Information</Button>
<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 7">Calculate Winnings</Button></td></tr> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_BuyTicket 0">Purchase Ticket</Button>
<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 9">View Past Results</Button></td></tr> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_ShowTickets">Calculate Winnings</Button>
<tr><td width=270><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Quest TeleportToRaceTrack" msg="1298;Monster Race Track">Exit the Monster Race Track</Button></td></tr> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_ViewHistory">View Past Results</Button>
</table> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Quest TeleportToRaceTrack" msg="1298;Monster Race Track">Exit the monster race track.</Button>
<img src="L2UI.SquareBlank" width=270 height=10> <img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareBlank" width=270 height=5> <img src="L2UI.SquareBlank" width=270 height=10>
<img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=270 height=5>
</body></html> </body></html>

View File

@@ -105,7 +105,6 @@ import handlers.admincommandhandlers.AdminMenu;
import handlers.admincommandhandlers.AdminMessages; import handlers.admincommandhandlers.AdminMessages;
import handlers.admincommandhandlers.AdminMissingHtmls; import handlers.admincommandhandlers.AdminMissingHtmls;
import handlers.admincommandhandlers.AdminMobGroup; import handlers.admincommandhandlers.AdminMobGroup;
import handlers.admincommandhandlers.AdminMonsterRace;
import handlers.admincommandhandlers.AdminOlympiad; import handlers.admincommandhandlers.AdminOlympiad;
import handlers.admincommandhandlers.AdminOnline; import handlers.admincommandhandlers.AdminOnline;
import handlers.admincommandhandlers.AdminPForge; import handlers.admincommandhandlers.AdminPForge;
@@ -442,7 +441,6 @@ public class MasterHandler
AdminMessages.class, AdminMessages.class,
AdminMissingHtmls.class, AdminMissingHtmls.class,
AdminMobGroup.class, AdminMobGroup.class,
AdminMonsterRace.class,
AdminOlympiad.class, AdminOlympiad.class,
AdminOnline.class, AdminOnline.class,
AdminPathNode.class, AdminPathNode.class,

View File

@@ -1,166 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package handlers.admincommandhandlers;
import org.l2jmobius.commons.concurrent.ThreadPool;
import org.l2jmobius.gameserver.MonsterRace;
import org.l2jmobius.gameserver.handler.IAdminCommandHandler;
import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
import org.l2jmobius.gameserver.network.SystemMessageId;
import org.l2jmobius.gameserver.network.serverpackets.DeleteObject;
import org.l2jmobius.gameserver.network.serverpackets.MonRaceInfo;
import org.l2jmobius.gameserver.network.serverpackets.PlaySound;
import org.l2jmobius.gameserver.network.serverpackets.SystemMessage;
/**
* This class handles following admin commands: - invul = turns invulnerability on/off
* @version $Revision: 1.1.6.4 $ $Date: 2007/07/31 10:06:00 $
*/
public class AdminMonsterRace implements IAdminCommandHandler
{
private static final String[] ADMIN_COMMANDS =
{
"admin_mons"
};
static int state = -1;
@Override
public boolean useAdminCommand(String command, PlayerInstance activeChar)
{
if (command.equalsIgnoreCase("admin_mons"))
{
handleSendPacket(activeChar);
}
return true;
}
@Override
public String[] getAdminCommandList()
{
return ADMIN_COMMANDS;
}
private void handleSendPacket(PlayerInstance activeChar)
{
/*
* -1 0 to initialize the race 0 15322 to start race 13765 -1 in middle of race -1 0 to end the race 8003 to 8027
*/
final int[][] codes =
{
{
-1,
0
},
{
0,
15322
},
{
13765,
-1
},
{
-1,
0
}
};
final MonsterRace race = MonsterRace.getInstance();
if (state == -1)
{
state++;
race.newRace();
race.newSpeeds();
final MonRaceInfo spk = new MonRaceInfo(codes[state][0], codes[state][1], race.getMonsters(), race.getSpeeds());
activeChar.sendPacket(spk);
activeChar.broadcastPacket(spk);
}
else if (state == 0)
{
state++;
final SystemMessage sm = new SystemMessage(SystemMessageId.THEY_RE_OFF);
sm.addInt(0);
activeChar.sendPacket(sm);
final PlaySound sRace = new PlaySound(1, "S_Race", 0, 0, 0, 0, 0);
activeChar.sendPacket(sRace);
activeChar.broadcastPacket(sRace);
final PlaySound sRace2 = new PlaySound(0, "ItemSound2.race_start", 1, 121209259, 12125, 182487, -3559);
activeChar.sendPacket(sRace2);
activeChar.broadcastPacket(sRace2);
final MonRaceInfo spk = new MonRaceInfo(codes[state][0], codes[state][1], race.getMonsters(), race.getSpeeds());
activeChar.sendPacket(spk);
activeChar.broadcastPacket(spk);
ThreadPool.schedule(new RunRace(codes, activeChar), 5000);
}
}
private class RunRace implements Runnable
{
private final int[][] codes;
private final PlayerInstance activeChar;
public RunRace(int[][] pCodes, PlayerInstance pActiveChar)
{
codes = pCodes;
activeChar = pActiveChar;
}
@Override
public void run()
{
// int[][] speeds1 = MonsterRace.getInstance().getSpeeds();
// MonsterRace.getInstance().newSpeeds();
// int[][] speeds2 = MonsterRace.getInstance().getSpeeds();
/*
* int[] speed = new int[8]; for (int i=0; i<8; i++) { for (int j=0; j<20; j++) { //LOGGER.info("Adding "+speeds1[i][j] +" and "+ speeds2[i][j]); speed[i] += (speeds1[i][j]*1); // + (speeds2[i][j]*1); } LOGGER.info("Total speed for "+(i+1)+" = "+speed[i]); }
*/
final MonRaceInfo spk = new MonRaceInfo(codes[2][0], codes[2][1], MonsterRace.getInstance().getMonsters(), MonsterRace.getInstance().getSpeeds());
activeChar.sendPacket(spk);
activeChar.broadcastPacket(spk);
ThreadPool.schedule(new RunEnd(activeChar), 30000);
}
}
private static class RunEnd implements Runnable
{
private final PlayerInstance activeChar;
public RunEnd(PlayerInstance pActiveChar)
{
activeChar = pActiveChar;
}
@Override
public void run()
{
DeleteObject obj = null;
for (int i = 0; i < 8; i++)
{
obj = new DeleteObject(MonsterRace.getInstance().getMonsters()[i]);
activeChar.sendPacket(obj);
activeChar.broadcastPacket(obj);
}
state = -1;
}
}
}

View File

@@ -145,6 +145,7 @@ import org.l2jmobius.gameserver.instancemanager.SiegeGuardManager;
import org.l2jmobius.gameserver.instancemanager.SiegeManager; import org.l2jmobius.gameserver.instancemanager.SiegeManager;
import org.l2jmobius.gameserver.instancemanager.WalkingManager; import org.l2jmobius.gameserver.instancemanager.WalkingManager;
import org.l2jmobius.gameserver.instancemanager.ZoneManager; import org.l2jmobius.gameserver.instancemanager.ZoneManager;
import org.l2jmobius.gameserver.instancemanager.games.MonsterRace;
import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.World;
import org.l2jmobius.gameserver.model.entity.Hero; import org.l2jmobius.gameserver.model.entity.Hero;
import org.l2jmobius.gameserver.model.events.EventDispatcher; import org.l2jmobius.gameserver.model.events.EventDispatcher;
@@ -406,10 +407,8 @@ public class GameServer
ItemsAutoDestroy.getInstance(); ItemsAutoDestroy.getInstance();
} }
if (Config.ALLOW_RACE)
{
MonsterRace.getInstance(); MonsterRace.getInstance();
}
TaskManager.getInstance(); TaskManager.getInstance();
AntiFeedManager.getInstance().registerEvent(AntiFeedManager.GAME_ID); AntiFeedManager.getInstance().registerEvent(AntiFeedManager.GAME_ID);

View File

@@ -1,140 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.data.xml.impl.NpcData;
import org.l2jmobius.gameserver.model.actor.Npc;
import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate;
public class MonsterRace
{
protected static final Logger LOGGER = Logger.getLogger(MonsterRace.class.getName());
private final Npc[] _monsters;
private int[][] _speeds;
private final int[] _first;
private final int[] _second;
protected MonsterRace()
{
_monsters = new Npc[8];
_speeds = new int[8][20];
_first = new int[2];
_second = new int[2];
}
public void newRace()
{
int random = 0;
for (int i = 0; i < 8; i++)
{
final int id = 31003;
random = Rnd.get(24);
while (true)
{
for (int j = i - 1; j >= 0; j--)
{
if (_monsters[j].getTemplate().getId() == (id + random))
{
random = Rnd.get(24);
}
}
break;
}
try
{
final NpcTemplate template = NpcData.getInstance().getTemplate(id + random);
_monsters[i] = (Npc) Class.forName("org.l2jmobius.gameserver.model.actor.instance." + template.getType() + "Instance").getConstructors()[0].newInstance(template);
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Unable to create monster!", e);
}
}
newSpeeds();
}
public void newSpeeds()
{
_speeds = new int[8][20];
int total = 0;
_first[1] = 0;
_second[1] = 0;
for (int i = 0; i < 8; i++)
{
total = 0;
for (int j = 0; j < 20; j++)
{
_speeds[i][j] = j == 19 ? 100 : Rnd.get(60) + 65;
total += _speeds[i][j];
}
if (total >= _first[1])
{
_second[0] = _first[0];
_second[1] = _first[1];
_first[0] = 8 - i;
_first[1] = total;
}
else if (total >= _second[1])
{
_second[0] = 8 - i;
_second[1] = total;
}
}
}
/**
* @return Returns the monsters.
*/
public Npc[] getMonsters()
{
return _monsters;
}
/**
* @return Returns the speeds.
*/
public int[][] getSpeeds()
{
return _speeds;
}
public int getFirstPlace()
{
return _first[0];
}
public int getSecondPlace()
{
return _second[0];
}
public static MonsterRace getInstance()
{
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder
{
protected static final MonsterRace INSTANCE = new MonsterRace();
}
}

View File

@@ -0,0 +1,623 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.instancemanager.games;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.l2jmobius.Config;
import org.l2jmobius.commons.concurrent.ThreadPool;
import org.l2jmobius.commons.database.DatabaseFactory;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.data.xml.impl.NpcData;
import org.l2jmobius.gameserver.model.actor.Npc;
import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate;
import org.l2jmobius.gameserver.model.zone.type.DerbyTrackZone;
import org.l2jmobius.gameserver.network.SystemMessageId;
import org.l2jmobius.gameserver.network.serverpackets.DeleteObject;
import org.l2jmobius.gameserver.network.serverpackets.MonRaceInfo;
import org.l2jmobius.gameserver.network.serverpackets.PlaySound;
import org.l2jmobius.gameserver.network.serverpackets.SystemMessage;
import org.l2jmobius.gameserver.util.Broadcast;
public class MonsterRace
{
protected static final Logger LOGGER = Logger.getLogger(MonsterRace.class.getName());
public static enum RaceState
{
ACCEPTING_BETS,
WAITING,
STARTING_RACE,
RACE_END
}
protected static final PlaySound SOUND_1 = new PlaySound(1, "S_Race", 0, 0, 0, 0, 0);
protected static final PlaySound SOUND_2 = new PlaySound("ItemSound2.race_start");
protected static final int[][] CODES =
{
{
-1,
0
},
{
0,
15322
},
{
13765,
-1
}
};
protected final List<Integer> _npcTemplates = new ArrayList<>(); // List holding npc templates, shuffled on a new race.
protected final List<HistoryInfo> _history = new ArrayList<>(); // List holding old race records.
protected final Map<Integer, Long> _betsPerLane = new ConcurrentHashMap<>(); // Map holding all bets for each lane ; values setted to 0 after every race.
protected final List<Double> _odds = new ArrayList<>(); // List holding sorted odds per lane ; cleared at new odds calculation.
protected int _raceNumber = 1;
protected int _finalCountdown = 0;
protected RaceState _state = RaceState.RACE_END;
protected MonRaceInfo _packet;
private final Npc[] _monsters = new Npc[8];
private int[][] _speeds = new int[8][20];
private final int[] _first = new int[2];
private final int[] _second = new int[2];
protected MonsterRace()
{
if (!Config.ALLOW_RACE)
{
return;
}
// Feed _history with previous race results.
loadHistory();
// Feed _betsPerLane with stored informations on bets.
loadBets();
// Feed _npcTemplates, we will only have to shuffle it when needed.
for (int i = 31003; i < 31027; i++)
{
_npcTemplates.add(i);
}
ThreadPool.scheduleAtFixedRate(new Announcement(), 0, 1000);
}
public static class HistoryInfo
{
private final int _raceId;
private int _first;
private int _second;
private double _oddRate;
public HistoryInfo(int raceId, int first, int second, double oddRate)
{
_raceId = raceId;
_first = first;
_second = second;
_oddRate = oddRate;
}
public int getRaceId()
{
return _raceId;
}
public int getFirst()
{
return _first;
}
public int getSecond()
{
return _second;
}
public double getOddRate()
{
return _oddRate;
}
public void setFirst(int first)
{
_first = first;
}
public void setSecond(int second)
{
_second = second;
}
public void setOddRate(double oddRate)
{
_oddRate = oddRate;
}
}
private class Announcement implements Runnable
{
public Announcement()
{
}
@Override
public void run()
{
if (_finalCountdown > 1200)
{
_finalCountdown = 0;
}
switch (_finalCountdown)
{
case 0:
{
newRace();
newSpeeds();
_state = RaceState.ACCEPTING_BETS;
_packet = new MonRaceInfo(CODES[0][0], CODES[0][1], getMonsters(), getSpeeds());
final SystemMessage msg = new SystemMessage(SystemMessageId.TICKETS_ARE_NOW_AVAILABLE_FOR_MONSTER_RACE_S1);
msg.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, _packet, msg);
break;
}
case 30: // 30 sec
case 60: // 1 min
case 90: // 1 min 30 sec
case 120: // 2 min
case 150: // 2 min 30
case 180: // 3 min
case 210: // 3 min 30
case 240: // 4 min
case 270: // 4 min 30 sec
case 330: // 5 min 30 sec
case 360: // 6 min
case 390: // 6 min 30 sec
case 420: // 7 min
case 450: // 7 min 30
case 480: // 8 min
case 510: // 8 min 30
case 540: // 9 min
case 570: // 9 min 30 sec
case 630: // 10 min 30 sec
case 660: // 11 min
case 690: // 11 min 30 sec
case 720: // 12 min
case 750: // 12 min 30
case 780: // 13 min
case 810: // 13 min 30
case 870: // 14 min 30 sec
{
final SystemMessage msg = new SystemMessage(SystemMessageId.TICKETS_ARE_NOW_AVAILABLE_FOR_MONSTER_RACE_S1);
msg.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg);
break;
}
case 300: // 5 min
{
final SystemMessage msg = new SystemMessage(SystemMessageId.NOW_SELLING_TICKETS_FOR_MONSTER_RACE_S1);
msg.addInt(_raceNumber);
final SystemMessage msg2 = new SystemMessage(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MIN);
msg2.addInt(10);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg, msg2);
break;
}
case 600: // 10 min
{
final SystemMessage msg = new SystemMessage(SystemMessageId.NOW_SELLING_TICKETS_FOR_MONSTER_RACE_S1);
msg.addInt(_raceNumber);
final SystemMessage msg2 = new SystemMessage(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MIN);
msg2.addInt(5);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg, msg2);
break;
}
case 840: // 14 min
{
final SystemMessage msg = new SystemMessage(SystemMessageId.NOW_SELLING_TICKETS_FOR_MONSTER_RACE_S1);
msg.addInt(_raceNumber);
final SystemMessage msg2 = new SystemMessage(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MIN);
msg2.addInt(1);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg, msg2);
break;
}
case 900: // 15 min
{
_state = RaceState.WAITING;
calculateOdds();
final SystemMessage msg = new SystemMessage(SystemMessageId.TICKETS_ARE_NOW_AVAILABLE_FOR_MONSTER_RACE_S1);
msg.addInt(_raceNumber);
final SystemMessage msg2 = new SystemMessage(SystemMessageId.TICKETS_SALES_ARE_CLOSED_FOR_MONSTER_RACE_S1_ODDS_ARE_POSTED);
msg2.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg, msg2);
break;
}
case 960: // 16 min
case 1020: // 17 min
{
final int minutes = (_finalCountdown == 960) ? 2 : 1;
final SystemMessage msg = new SystemMessage(SystemMessageId.MONSTER_RACE_S2_WILL_BEGIN_IN_S1_MIN);
msg.addInt(minutes);
msg.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg);
break;
}
case 1050: // 17 min 30 sec
{
final SystemMessage msg = new SystemMessage(SystemMessageId.MONSTER_RACE_S1_WILL_BEGIN_IN_30_SEC);
msg.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg);
break;
}
case 1070: // 17 min 50 sec
{
final SystemMessage msg = new SystemMessage(SystemMessageId.MONSTER_RACE_S1_IS_ABOUT_TO_BEGIN_COUNTDOWN_IN_FIVE_SEC);
msg.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg);
break;
}
case 1075: // 17 min 55 sec
case 1076: // 17 min 56 sec
case 1077: // 17 min 57 sec
case 1078: // 17 min 58 sec
case 1079: // 17 min 59 sec
{
final int seconds = 1080 - _finalCountdown;
final SystemMessage msg = new SystemMessage(SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SEC);
msg.addInt(seconds);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg);
break;
}
case 1080: // 18 min
{
_state = RaceState.STARTING_RACE;
_packet = new MonRaceInfo(CODES[1][0], CODES[1][1], getMonsters(), getSpeeds());
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, new SystemMessage(SystemMessageId.THEY_RE_OFF), SOUND_1, SOUND_2, _packet);
break;
}
case 1085: // 18 min 5 sec
{
_packet = new MonRaceInfo(CODES[2][0], CODES[2][1], getMonsters(), getSpeeds());
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, _packet);
break;
}
case 1115: // 18 min 35 sec
{
_state = RaceState.RACE_END;
// Populate history info with data, stores it in database.
final HistoryInfo info = _history.get(_history.size() - 1);
info.setFirst(getFirstPlace());
info.setSecond(getSecondPlace());
info.setOddRate(_odds.get(getFirstPlace() - 1));
saveHistory(info);
clearBets();
final SystemMessage msg = new SystemMessage(SystemMessageId.FIRST_PRIZE_GOES_TO_THE_PLAYER_IN_LANE_S1_SECOND_PRIZE_GOES_TO_THE_PLAYER_IN_LANE_S2);
msg.addInt(getFirstPlace());
msg.addInt(getSecondPlace());
final SystemMessage msg2 = new SystemMessage(SystemMessageId.MONSTER_RACE_S1_IS_FINISHED);
msg2.addInt(_raceNumber);
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, msg, msg2);
_raceNumber++;
break;
}
case 1140: // 19 min
{
Broadcast.toAllPlayersInZoneType(DerbyTrackZone.class, new DeleteObject(getMonsters()[0]), new DeleteObject(getMonsters()[1]), new DeleteObject(getMonsters()[2]), new DeleteObject(getMonsters()[3]), new DeleteObject(getMonsters()[4]), new DeleteObject(getMonsters()[5]), new DeleteObject(getMonsters()[6]), new DeleteObject(getMonsters()[7]));
break;
}
}
_finalCountdown += 1;
}
}
public void newRace()
{
// Edit _history.
_history.add(new HistoryInfo(_raceNumber, 0, 0, 0));
// Randomize _npcTemplates.
Collections.shuffle(_npcTemplates);
// Setup 8 new creatures ; pickup the first 8 from _npcTemplates.
for (int i = 0; i < 8; i++)
{
try
{
final NpcTemplate template = NpcData.getInstance().getTemplate(_npcTemplates.get(i));
_monsters[i] = (Npc) Class.forName("org.l2jmobius.gameserver.model.actor.instance." + template.getType() + "Instance").getConstructors()[0].newInstance(template);
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "", e);
}
}
}
public void newSpeeds()
{
_speeds = new int[8][20];
int total = 0;
_first[1] = 0;
_second[1] = 0;
for (int i = 0; i < 8; i++)
{
total = 0;
for (int j = 0; j < 20; j++)
{
if (j == 19)
{
_speeds[i][j] = 100;
}
else
{
_speeds[i][j] = Rnd.get(60) + 65;
}
total += _speeds[i][j];
}
if (total >= _first[1])
{
_second[0] = _first[0];
_second[1] = _first[1];
_first[0] = 8 - i;
_first[1] = total;
}
else if (total >= _second[1])
{
_second[0] = 8 - i;
_second[1] = total;
}
}
}
/**
* Load past races informations, feeding _history arrayList.<br>
* Also sets _raceNumber, based on latest HistoryInfo loaded.
*/
protected void loadHistory()
{
try (Connection con = DatabaseFactory.getConnection())
{
PreparedStatement statement = con.prepareStatement("SELECT * FROM mdt_history");
ResultSet rset = statement.executeQuery();
while (rset.next())
{
_history.add(new HistoryInfo(rset.getInt("race_id"), rset.getInt("first"), rset.getInt("second"), rset.getDouble("odd_rate")));
_raceNumber++;
}
rset.close();
statement.close();
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, "MonsterRace: Can't load history: " + e.getMessage(), e);
}
LOGGER.info("MonsterRace: loaded " + _history.size() + " records, currently on race #" + _raceNumber);
}
/**
* Save an history record into database.
* @param history The infos to store.
*/
protected void saveHistory(HistoryInfo history)
{
try (Connection con = DatabaseFactory.getConnection())
{
PreparedStatement statement = con.prepareStatement("INSERT INTO mdt_history (race_id, first, second, odd_rate) VALUES (?,?,?,?)");
statement.setInt(1, history.getRaceId());
statement.setInt(2, history.getFirst());
statement.setInt(3, history.getSecond());
statement.setDouble(4, history.getOddRate());
statement.execute();
statement.close();
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, "MonsterRace: Can't save history: " + e.getMessage(), e);
}
}
/**
* Load current bets per lane ; initialize the map keys.
*/
protected void loadBets()
{
try (Connection con = DatabaseFactory.getConnection())
{
PreparedStatement statement = con.prepareStatement("SELECT * FROM mdt_bets");
ResultSet rset = statement.executeQuery();
while (rset.next())
{
setBetOnLane(rset.getInt("lane_id"), rset.getLong("bet"), false);
}
rset.close();
statement.close();
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, "MonsterRace: Can't load bets: " + e.getMessage(), e);
}
}
/**
* Save the current lane bet into database.
* @param lane : The lane to affect.
* @param sum : The sum to set.
*/
protected void saveBet(int lane, long sum)
{
try (Connection con = DatabaseFactory.getConnection())
{
PreparedStatement statement = con.prepareStatement("REPLACE INTO mdt_bets (lane_id, bet) VALUES (?,?)");
statement.setInt(1, lane);
statement.setLong(2, sum);
statement.execute();
statement.close();
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, "MonsterRace: Can't save bet: " + e.getMessage(), e);
}
}
/**
* Clear all lanes bets, either on database or Map.
*/
protected void clearBets()
{
for (int key : _betsPerLane.keySet())
{
_betsPerLane.put(key, 0L);
}
try (Connection con = DatabaseFactory.getConnection())
{
PreparedStatement statement = con.prepareStatement("UPDATE mdt_bets SET bet = 0");
statement.execute();
statement.close();
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, "MonsterRace: Can't clear bets: " + e.getMessage(), e);
}
}
/**
* Setup lane bet, based on previous value (if any).
* @param lane : The lane to edit.
* @param amount : The amount to add.
* @param saveOnDb : Should it be saved on db or not.
*/
public void setBetOnLane(int lane, long amount, boolean saveOnDb)
{
final long sum = (_betsPerLane.containsKey(lane)) ? _betsPerLane.get(lane) + amount : amount;
_betsPerLane.put(lane, sum);
if (saveOnDb)
{
saveBet(lane, sum);
}
}
/**
* Calculate odds for every lane, based on others lanes.
*/
protected void calculateOdds()
{
// Clear previous List holding old odds.
_odds.clear();
// Sort bets lanes per lane.
final Map<Integer, Long> sortedLanes = new TreeMap<>(_betsPerLane);
// Pass a first loop in order to calculate total sum of all lanes.
long sumOfAllLanes = 0;
for (long amount : sortedLanes.values())
{
sumOfAllLanes += amount;
}
// As we get the sum, we can now calculate the odd rate of each lane.
for (long amount : sortedLanes.values())
{
_odds.add((amount == 0) ? 0D : Math.max(1.25, (sumOfAllLanes * 0.7) / amount));
}
}
public Npc[] getMonsters()
{
return _monsters;
}
public int[][] getSpeeds()
{
return _speeds;
}
public int getFirstPlace()
{
return _first[0];
}
public int getSecondPlace()
{
return _second[0];
}
public MonRaceInfo getRacePacket()
{
return _packet;
}
public RaceState getCurrentRaceState()
{
return _state;
}
public int getRaceNumber()
{
return _raceNumber;
}
public List<HistoryInfo> getHistory()
{
return _history;
}
public List<Double> getOdds()
{
return _odds;
}
public static MonsterRace getInstance()
{
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder
{
protected static final MonsterRace INSTANCE = new MonsterRace();
}
}

View File

@@ -16,69 +16,26 @@
*/ */
package org.l2jmobius.gameserver.model.actor.instance; package org.l2jmobius.gameserver.model.actor.instance;
import java.util.Collection; import java.util.List;
import java.util.concurrent.ConcurrentHashMap; import java.util.Locale;
import org.l2jmobius.Config; import org.l2jmobius.Config;
import org.l2jmobius.commons.concurrent.ThreadPool; import org.l2jmobius.commons.util.StringUtil;
import org.l2jmobius.gameserver.MonsterRace;
import org.l2jmobius.gameserver.enums.InstanceType;
import org.l2jmobius.gameserver.idfactory.IdFactory; import org.l2jmobius.gameserver.idfactory.IdFactory;
import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.instancemanager.games.MonsterRace;
import org.l2jmobius.gameserver.instancemanager.games.MonsterRace.HistoryInfo;
import org.l2jmobius.gameserver.instancemanager.games.MonsterRace.RaceState;
import org.l2jmobius.gameserver.model.actor.Npc; import org.l2jmobius.gameserver.model.actor.Npc;
import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate; import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate;
import org.l2jmobius.gameserver.model.itemcontainer.Inventory;
import org.l2jmobius.gameserver.model.items.instance.ItemInstance; import org.l2jmobius.gameserver.model.items.instance.ItemInstance;
import org.l2jmobius.gameserver.network.SystemMessageId; import org.l2jmobius.gameserver.network.SystemMessageId;
import org.l2jmobius.gameserver.network.serverpackets.ActionFailed; import org.l2jmobius.gameserver.network.serverpackets.ActionFailed;
import org.l2jmobius.gameserver.network.serverpackets.DeleteObject;
import org.l2jmobius.gameserver.network.serverpackets.IClientOutgoingPacket;
import org.l2jmobius.gameserver.network.serverpackets.InventoryUpdate;
import org.l2jmobius.gameserver.network.serverpackets.MonRaceInfo;
import org.l2jmobius.gameserver.network.serverpackets.NpcHtmlMessage; import org.l2jmobius.gameserver.network.serverpackets.NpcHtmlMessage;
import org.l2jmobius.gameserver.network.serverpackets.PlaySound;
import org.l2jmobius.gameserver.network.serverpackets.SystemMessage; import org.l2jmobius.gameserver.network.serverpackets.SystemMessage;
import org.l2jmobius.gameserver.util.Broadcast;
public class RaceManagerInstance extends Npc public class RaceManagerInstance extends Npc
{ {
public static final int LANES = 8; protected static final int TICKET_PRICES[] =
public static final int WINDOW_START = 0;
private static Collection<RaceManagerInstance> _managers;
protected static int _raceNumber = 4;
// Time Constants
private static final long SECOND = 1000;
private static final long MINUTE = 60 * SECOND;
private static int _minutes = 5;
// States
private static final int ACCEPTING_BETS = 0;
private static final int WAITING = 1;
private static final int STARTING_RACE = 2;
private static final int RACE_END = 3;
private static int _state = RACE_END;
protected static final int[][] _codes =
{
{
-1,
0
},
{
0,
15322
},
{
13765,
-1
}
};
private static boolean _notInitialized = true;
protected static MonRaceInfo _packet;
protected static final int[] _cost =
{ {
100, 100,
500, 500,
@@ -93,223 +50,167 @@ public class RaceManagerInstance extends Npc
public RaceManagerInstance(NpcTemplate template) public RaceManagerInstance(NpcTemplate template)
{ {
super(template); super(template);
setInstanceType(InstanceType.RaceManagerInstance);
if (!Config.ALLOW_RACE)
{
return;
}
if (_notInitialized)
{
_notInitialized = false;
_managers = ConcurrentHashMap.newKeySet();
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKETS_ARE_NOW_AVAILABLE_FOR_MONSTER_RACE_S1), 0, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.NOW_SELLING_TICKETS_FOR_MONSTER_RACE_S1), 30 * SECOND, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKETS_ARE_NOW_AVAILABLE_FOR_MONSTER_RACE_S1), MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.NOW_SELLING_TICKETS_FOR_MONSTER_RACE_S1), MINUTE + (30 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MIN), 2 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MIN), 3 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MIN), 4 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MIN), 5 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MIN), 6 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.TICKETS_SALES_ARE_CLOSED_FOR_MONSTER_RACE_S1_ODDS_ARE_POSTED), 7 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.MONSTER_RACE_S2_WILL_BEGIN_IN_S1_MIN), 7 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.MONSTER_RACE_S2_WILL_BEGIN_IN_S1_MIN), 8 * MINUTE, 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.MONSTER_RACE_S1_WILL_BEGIN_IN_30_SEC), (8 * MINUTE) + (30 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.MONSTER_RACE_S1_IS_ABOUT_TO_BEGIN_COUNTDOWN_IN_FIVE_SEC), (8 * MINUTE) + (50 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SEC), (8 * MINUTE) + (55 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SEC), (8 * MINUTE) + (56 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SEC), (8 * MINUTE) + (57 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SEC), (8 * MINUTE) + (58 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SEC), (8 * MINUTE) + (59 * SECOND), 10 * MINUTE);
ThreadPool.scheduleAtFixedRate(new Announcement(SystemMessageId.THEY_RE_OFF), 9 * MINUTE, 10 * MINUTE);
}
_managers.add(this);
}
@Override
public boolean isAutoAttackable(Creature attacker)
{
if (attacker.isMonster())
{
return true;
}
return super.isAutoAttackable(attacker);
}
class Announcement implements Runnable
{
private final SystemMessageId _type;
public Announcement(SystemMessageId pType)
{
_type = pType;
}
@Override
public void run()
{
makeAnnouncement(_type);
}
}
public void makeAnnouncement(SystemMessageId type)
{
final SystemMessage sm = new SystemMessage(type);
switch (type.getId())
{
case 816: // SystemMessageId.TICKETS_ARE_NOW_AVAILABLE_FOR_MONSTER_RACE_S1
case 817: // SystemMessageId.NOW_SELLING_TICKETS_FOR_MONSTER_RACE_S1
{
if (_state != ACCEPTING_BETS)
{// LOGGER.info("Race Initializing");
_state = ACCEPTING_BETS;
startRace();
} // else{LOGGER.info("Race open");}
sm.addInt(_raceNumber);
break;
}
case 818: // SystemMessageId.TICKET_SALES_FOR_THE_MONSTER_RACE_WILL_END_IN_S1_MINUTE_S
case 820: // SystemMessageId.MONSTER_RACE_S2_WILL_BEGIN_IN_S1_MINUTE_S
case 823: // SystemMessageId.THE_RACE_WILL_BEGIN_IN_S1_SECOND_S
{
sm.addInt(_minutes);
if (type.getId() == 820)
{
sm.addInt(_raceNumber);
}
_minutes--;
break;
}
case 819: // SystemMessageId.TICKETS_SALES_ARE_CLOSED_FOR_MONSTER_RACE_S1_ODDS_ARE_POSTED
{
// LOGGER.info("Sales closed");
sm.addInt(_raceNumber);
_state = WAITING;
_minutes = 2;
break;
}
case 821: // SystemMessageId.MONSTER_RACE_S1_WILL_BEGIN_IN_30_SECONDS
case 822: // SystemMessageId.MONSTER_RACE_S1_IS_ABOUT_TO_BEGIN_COUNTDOWN_IN_FIVE_SECONDS
case 825: // SystemMessageId.MONSTER_RACE_S1_IS_FINISHED
{
sm.addInt(_raceNumber);
_minutes = 5;
break;
}
case 826: // SystemMessageId.FIRST_PRIZE_GOES_TO_THE_PLAYER_IN_LANE_S1_SECOND_PRIZE_GOES_TO_THE_PLAYER_IN_LANE_S2
{
// LOGGER.info("Placing");
_state = RACE_END;
sm.addInt(MonsterRace.getInstance().getFirstPlace());
sm.addInt(MonsterRace.getInstance().getSecondPlace());
break;
}
}
// _logn.info("Counter: "+minutes);
// LOGGER.info("State: "+state);
broadcast(sm);
// LOGGER.info("Player's known: "+getKnownPlayers().size());
if (type == SystemMessageId.THEY_RE_OFF)
{
// LOGGER.info("Starting race");
_state = STARTING_RACE;
startRace();
_minutes = 5;
}
}
protected void broadcast(IClientOutgoingPacket pkt)
{
for (RaceManagerInstance manager : _managers)
{
if (!manager.isDead())
{
Broadcast.toKnownPlayers(manager, pkt);
}
}
}
public void sendMonsterInfo()
{
broadcast(_packet);
}
private void startRace()
{
final MonsterRace race = MonsterRace.getInstance();
if (_state == STARTING_RACE)
{
// state++;
broadcast(new PlaySound(1, "S_Race", 0, 0, 0, 0, 0));
broadcast(new PlaySound(0, "ItemSound2.race_start", 1, 121209259, 12125, 182487, -3559));
_packet = new MonRaceInfo(_codes[1][0], _codes[1][1], race.getMonsters(), race.getSpeeds());
sendMonsterInfo();
ThreadPool.schedule(new RunRace(), 5000);
}
else
{
// state++;
race.newRace();
race.newSpeeds();
_packet = new MonRaceInfo(_codes[0][0], _codes[0][1], race.getMonsters(), race.getSpeeds());
sendMonsterInfo();
}
} }
@Override @Override
public void onBypassFeedback(PlayerInstance player, String command) public void onBypassFeedback(PlayerInstance player, String command)
{ {
if (command.startsWith("BuyTicket") && (_state != ACCEPTING_BETS))
{
if (!Config.ALLOW_RACE)
{
return;
}
player.sendPacket(SystemMessageId.MONSTER_RACE_TICKETS_ARE_NO_LONGER_AVAILABLE);
command = "Chat 0";
}
if (command.startsWith("ShowOdds") && (_state == ACCEPTING_BETS))
{
if (!Config.ALLOW_RACE)
{
return;
}
player.sendPacket(SystemMessageId.MONSTER_RACE_PAYOUT_INFORMATION_IS_NOT_AVAILABLE_WHILE_TICKETS_ARE_BEING_SOLD);
command = "Chat 0";
}
if (command.startsWith("BuyTicket")) if (command.startsWith("BuyTicket"))
{ {
if (!Config.ALLOW_RACE) if (!Config.ALLOW_RACE || (MonsterRace.getInstance().getCurrentRaceState() != RaceState.ACCEPTING_BETS))
{ {
player.sendPacket(SystemMessageId.MONSTER_RACE_TICKETS_ARE_NO_LONGER_AVAILABLE);
super.onBypassFeedback(player, "Chat 0");
return; return;
} }
int val = Integer.parseInt(command.substring(10)); int val = Integer.parseInt(command.substring(10));
if (val == 0) if (val == 0)
{ {
player.setRace(0, 0); player.setRace(0, 0);
player.setRace(1, 0); player.setRace(1, 0);
} }
if (((val == 10) && (player.getRace(0) == 0)) || ((val == 20) && (player.getRace(0) == 0) && (player.getRace(1) == 0))) if (((val == 10) && (player.getRace(0) == 0)) || ((val == 20) && (player.getRace(0) == 0) && (player.getRace(1) == 0)))
{ {
val = 0; val = 0;
} }
showBuyTicket(player, val);
} String search, replace;
else if (command.equals("ShowOdds"))
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
if (val < 10)
{ {
if (!Config.ALLOW_RACE) html.setFile(player, getHtmlPath(getId(), 2, player));
for (int i = 0; i < 8; i++)
{
int n = i + 1;
search = "Mob" + n;
html.replace(search, MonsterRace.getInstance().getMonsters()[i].getTemplate().getName());
}
search = "No1";
if (val == 0)
{
html.replace(search, "");
}
else
{
html.replace(search, val);
player.setRace(0, val);
}
}
else if (val < 20)
{
if (player.getRace(0) == 0)
{ {
return; return;
} }
showOdds(player);
html.setFile(player, getHtmlPath(getId(), 3, player));
html.replace("0place", player.getRace(0));
search = "Mob1";
replace = MonsterRace.getInstance().getMonsters()[player.getRace(0) - 1].getTemplate().getName();
html.replace(search, replace);
search = "0adena";
if (val == 10)
{
html.replace(search, "");
}
else
{
html.replace(search, TICKET_PRICES[val - 11]);
player.setRace(1, val - 10);
}
}
else if (val == 20)
{
if ((player.getRace(0) == 0) || (player.getRace(1) == 0))
{
return;
}
html.setFile(player, getHtmlPath(getId(), 4, player));
html.replace("0place", player.getRace(0));
search = "Mob1";
replace = MonsterRace.getInstance().getMonsters()[player.getRace(0) - 1].getTemplate().getName();
html.replace(search, replace);
search = "0adena";
int price = TICKET_PRICES[player.getRace(1) - 1];
html.replace(search, price);
search = "0tax";
int tax = 0;
html.replace(search, tax);
search = "0total";
int total = price + tax;
html.replace(search, total);
}
else
{
if ((player.getRace(0) == 0) || (player.getRace(1) == 0))
{
return;
}
int ticket = player.getRace(0);
int priceId = player.getRace(1);
if (!player.reduceAdena("Race", TICKET_PRICES[priceId - 1], this, true))
{
return;
}
player.setRace(0, 0);
player.setRace(1, 0);
ItemInstance item = new ItemInstance(IdFactory.getInstance().getNextId(), 4443);
item.setCount(1);
item.setEnchantLevel(MonsterRace.getInstance().getRaceNumber());
item.setCustomType1(ticket);
item.setCustomType2(TICKET_PRICES[priceId - 1] / 100);
player.addItem("Race", item, player, false);
final SystemMessage msg = new SystemMessage(SystemMessageId.ACQUIRED_S1_S2);
msg.addInt(MonsterRace.getInstance().getRaceNumber());
msg.addItemName(4443);
player.sendPacket(msg);
// Refresh lane bet.
MonsterRace.getInstance().setBetOnLane(ticket, TICKET_PRICES[priceId - 1], true);
super.onBypassFeedback(player, "Chat 0");
return;
}
html.replace("1race", MonsterRace.getInstance().getRaceNumber());
html.replace("%objectId%", getObjectId());
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
else if (command.equals("ShowOdds"))
{
if (!Config.ALLOW_RACE || (MonsterRace.getInstance().getCurrentRaceState() == RaceState.ACCEPTING_BETS))
{
player.sendPacket(SystemMessageId.MONSTER_RACE_PAYOUT_INFORMATION_IS_NOT_AVAILABLE_WHILE_TICKETS_ARE_BEING_SOLD);
super.onBypassFeedback(player, "Chat 0");
return;
}
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
html.setFile(player, getHtmlPath(getId(), 5, player));
for (int i = 0; i < 8; i++)
{
final int n = i + 1;
html.replace("Mob" + n, MonsterRace.getInstance().getMonsters()[i].getTemplate().getName());
// Odd
final double odd = MonsterRace.getInstance().getOdds().get(i);
html.replace("Odd" + n, (odd > 0D) ? String.format(Locale.ENGLISH, "%.1f", odd) : "&$804;");
}
html.replace("1race", MonsterRace.getInstance().getRaceNumber());
html.replace("%objectId%", getObjectId());
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
} }
else if (command.equals("ShowInfo")) else if (command.equals("ShowInfo"))
{ {
@@ -317,269 +218,160 @@ public class RaceManagerInstance extends Npc
{ {
return; return;
} }
showMonsterInfo(player);
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
html.setFile(player, getHtmlPath(getId(), 6, player));
for (int i = 0; i < 8; i++)
{
int n = i + 1;
String search = "Mob" + n;
html.replace(search, MonsterRace.getInstance().getMonsters()[i].getTemplate().getName());
} }
else if (command.equals("calculateWin")) html.replace("%objectId%", getObjectId());
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
else if (command.equals("ShowTickets"))
{ {
if (!Config.ALLOW_RACE) if (!Config.ALLOW_RACE)
{ {
super.onBypassFeedback(player, "Chat 0");
return; return;
} }
// displayCalculateWinnings(player);
// Generate data.
final StringBuilder sb = new StringBuilder();
// Retrieve player's tickets.
for (ItemInstance ticket : player.getInventory().getAllItemsByItemId(4443))
{
// Don't list current race tickets.
if (ticket.getEnchantLevel() == MonsterRace.getInstance().getRaceNumber())
{
continue;
} }
else if (command.equals("viewHistory"))
StringUtil.append(sb, "<tr><td><a action=\"bypass -h npc_%objectId%_ShowTicket ", "" + ticket.getObjectId(), "\">", "" + ticket.getEnchantLevel(), " Race Number</a></td><td align=right><font color=\"LEVEL\">", "" + ticket.getCustomType1(), "</font> Number</td><td align=right><font color=\"LEVEL\">", "" + (ticket.getCustomType2() * 100), "</font> Adena</td></tr>");
}
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
html.setFile(player, getHtmlPath(getId(), 7, player));
html.replace("%tickets%", sb.toString());
html.replace("%objectId%", getObjectId());
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
else if (command.startsWith("ShowTicket"))
{
// Retrieve ticket objectId.
final int val = Integer.parseInt(command.substring(11));
if (!Config.ALLOW_RACE || (val == 0))
{
super.onBypassFeedback(player, "Chat 0");
return;
}
// Retrieve ticket on player's inventory.
final ItemInstance ticket = player.getInventory().getItemByObjectId(val);
if (ticket == null)
{
super.onBypassFeedback(player, "Chat 0");
return;
}
final int raceId = ticket.getEnchantLevel();
final int lane = ticket.getCustomType1();
final int bet = ticket.getCustomType2() * 100;
// Retrieve HistoryInfo for that race.
final HistoryInfo info = MonsterRace.getInstance().getHistory().get(raceId - 1);
if (info == null)
{
super.onBypassFeedback(player, "Chat 0");
return;
}
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
html.setFile(player, getHtmlPath(getId(), 8, player));
html.replace("%raceId%", raceId);
html.replace("%lane%", lane);
html.replace("%bet%", bet);
html.replace("%firstLane%", info.getFirst());
html.replace("%odd%", (lane == info.getFirst()) ? String.format(Locale.ENGLISH, "%.2f", info.getOddRate()) : "0.01");
html.replace("%objectId%", getObjectId());
html.replace("%ticketObjectId%", val);
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
else if (command.startsWith("CalculateWin"))
{
// Retrieve ticket objectId.
final int val = Integer.parseInt(command.substring(13));
if (!Config.ALLOW_RACE || (val == 0))
{
super.onBypassFeedback(player, "Chat 0");
return;
}
// Delete ticket on player's inventory.
final ItemInstance ticket = player.getInventory().getItemByObjectId(val);
if (ticket == null)
{
super.onBypassFeedback(player, "Chat 0");
return;
}
final int raceId = ticket.getEnchantLevel();
final int lane = ticket.getCustomType1();
final int bet = ticket.getCustomType2() * 100;
// Retrieve HistoryInfo for that race.
final HistoryInfo info = MonsterRace.getInstance().getHistory().get(raceId - 1);
if (info == null)
{
super.onBypassFeedback(player, "Chat 0");
return;
}
// Destroy the ticket.
if (player.destroyItem("MonsterTrack", ticket, this, true))
{
player.addAdena("MonsterTrack", (int) (bet * ((lane == info.getFirst()) ? info.getOddRate() : 0.01)), this, true);
}
super.onBypassFeedback(player, "Chat 0");
return;
}
else if (command.equals("ViewHistory"))
{ {
if (!Config.ALLOW_RACE) if (!Config.ALLOW_RACE)
{ {
super.onBypassFeedback(player, "Chat 0");
return; return;
} }
// displayHistory(player);
// Generate data.
final StringBuilder sb = new StringBuilder();
// Use whole history, pickup from 'last element' and stop at 'latest element - 7'.
final List<HistoryInfo> history = MonsterRace.getInstance().getHistory();
for (int i = history.size() - 1; i >= Math.max(0, history.size() - 7); i--)
{
final HistoryInfo info = history.get(i);
StringUtil.append(sb, "<tr><td><font color=\"LEVEL\">", "" + info.getRaceId(), "</font> th</td><td><font color=\"LEVEL\">", "" + info.getFirst(), "</font> Lane </td><td><font color=\"LEVEL\">", "" + info.getSecond(), "</font> Lane</td><td align=right><font color=00ffff>", String.format(Locale.ENGLISH, "%.2f", info.getOddRate()), "</font> Times</td></tr>");
}
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
html.setFile(player, getHtmlPath(getId(), 9, player));
html.replace("%infos%", sb.toString());
html.replace("%objectId%", getObjectId());
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
} }
else else
{ {
// getKnownList().removeKnownObject(player);
super.onBypassFeedback(player, command); super.onBypassFeedback(player, command);
} }
} }
public void showOdds(PlayerInstance player)
{
if (_state == ACCEPTING_BETS)
{
return;
}
final int npcId = getTemplate().getId();
String filename;
String search;
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
filename = getHtmlPath(npcId, 5, player);
html.setFile(player, filename);
for (int i = 0; i < 8; i++)
{
final int n = i + 1;
search = "Mob" + n;
html.replace(search, MonsterRace.getInstance().getMonsters()[i].getTemplate().getName());
}
html.replace("1race", String.valueOf(_raceNumber));
html.replace("%objectId%", String.valueOf(getObjectId()));
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
public void showMonsterInfo(PlayerInstance player)
{
final int npcId = getTemplate().getId();
String filename;
String search;
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
filename = getHtmlPath(npcId, 6, player);
html.setFile(player, filename);
for (int i = 0; i < 8; i++)
{
final int n = i + 1;
search = "Mob" + n;
html.replace(search, MonsterRace.getInstance().getMonsters()[i].getTemplate().getName());
}
html.replace("%objectId%", String.valueOf(getObjectId()));
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
public void showBuyTicket(PlayerInstance player, int value)
{
if (_state != ACCEPTING_BETS)
{
return;
}
final int npcId = getTemplate().getId();
SystemMessage sm;
String filename;
String search;
String replace;
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
if (value < 10)
{
filename = getHtmlPath(npcId, 2, player);
html.setFile(player, filename);
for (int i = 0; i < 8; i++)
{
final int n = i + 1;
search = "Mob" + n;
html.replace(search, MonsterRace.getInstance().getMonsters()[i].getTemplate().getName());
}
search = "No1";
if (value == 0)
{
html.replace(search, "");
}
else
{
html.replace(search, Integer.toString(value));
player.setRace(0, value);
}
}
else if (value < 20)
{
if (player.getRace(0) == 0)
{
return;
}
filename = getHtmlPath(npcId, 3, player);
html.setFile(player, filename);
html.replace("0place", Integer.toString(player.getRace(0)));
search = "Mob1";
replace = MonsterRace.getInstance().getMonsters()[player.getRace(0) - 1].getTemplate().getName();
html.replace(search, replace);
search = "0adena";
if (value == 10)
{
html.replace(search, "");
}
else
{
html.replace(search, Integer.toString(_cost[value - 11]));
player.setRace(1, value - 10);
}
}
else if (value == 20)
{
if ((player.getRace(0) == 0) || (player.getRace(1) == 0))
{
return;
}
filename = getHtmlPath(npcId, 4, player);
html.setFile(player, filename);
html.replace("0place", Integer.toString(player.getRace(0)));
search = "Mob1";
replace = MonsterRace.getInstance().getMonsters()[player.getRace(0) - 1].getTemplate().getName();
html.replace(search, replace);
search = "0adena";
final int price = _cost[player.getRace(1) - 1];
html.replace(search, Integer.toString(price));
search = "0tax";
final int tax = 0;
html.replace(search, Integer.toString(tax));
search = "0total";
final int total = price + tax;
html.replace(search, Integer.toString(total));
}
else
{
if ((player.getRace(0) == 0) || (player.getRace(1) == 0))
{
return;
}
final int ticket = player.getRace(0);
final int priceId = player.getRace(1);
if (!player.reduceAdena("Race", _cost[priceId - 1], this, true))
{
return;
}
player.setRace(0, 0);
player.setRace(1, 0);
sm = new SystemMessage(SystemMessageId.ACQUIRED_S1_S2);
sm.addInt(_raceNumber);
sm.addItemName(4443);
player.sendPacket(sm);
final ItemInstance item = new ItemInstance(IdFactory.getInstance().getNextId(), 4443);
item.setCount(1);
item.setEnchantLevel(_raceNumber);
item.setCustomType1(ticket);
item.setCustomType2(_cost[priceId - 1] / 100);
player.getInventory().addItem("Race", item, player, this);
final InventoryUpdate iu = new InventoryUpdate();
iu.addItem(item);
final ItemInstance adenaupdate = player.getInventory().getItemByItemId(Inventory.ADENA_ID);
iu.addModifiedItem(adenaupdate);
player.sendInventoryUpdate(iu);
return;
}
html.replace("1race", String.valueOf(_raceNumber));
html.replace("%objectId%", String.valueOf(getObjectId()));
player.sendPacket(html);
player.sendPacket(ActionFailed.STATIC_PACKET);
}
public static class Race
{
private final Info[] _info;
public Race(Info[] pInfo)
{
_info = pInfo;
}
public Info getLaneInfo(int lane)
{
return _info[lane];
}
public class Info
{
private final int _id;
private final int _place;
private final int _odds;
private final int _payout;
public Info(int pId, int pPlace, int pOdds, int pPayout)
{
_id = pId;
_place = pPlace;
_odds = pOdds;
_payout = pPayout;
}
public int getId()
{
return _id;
}
public int getOdds()
{
return _odds;
}
public int getPayout()
{
return _payout;
}
public int getPlace()
{
return _place;
}
}
}
class RunRace implements Runnable
{
@Override
public void run()
{
_packet = new MonRaceInfo(_codes[2][0], _codes[2][1], MonsterRace.getInstance().getMonsters(), MonsterRace.getInstance().getSpeeds());
sendMonsterInfo();
ThreadPool.schedule(new RunEnd(), 30000);
}
}
class RunEnd implements Runnable
{
@Override
public void run()
{
makeAnnouncement(SystemMessageId.FIRST_PRIZE_GOES_TO_THE_PLAYER_IN_LANE_S1_SECOND_PRIZE_GOES_TO_THE_PLAYER_IN_LANE_S2);
makeAnnouncement(SystemMessageId.MONSTER_RACE_S1_IS_FINISHED);
_raceNumber++;
DeleteObject obj = null;
for (int i = 0; i < 8; i++)
{
obj = new DeleteObject(MonsterRace.getInstance().getMonsters()[i]);
broadcast(obj);
MonsterRace.getInstance().getMonsters()[i].deleteMe();
}
}
}
} }

View File

@@ -20,10 +20,12 @@ import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.l2jmobius.gameserver.enums.ChatType; import org.l2jmobius.gameserver.enums.ChatType;
import org.l2jmobius.gameserver.instancemanager.ZoneManager;
import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.World;
import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.actor.Creature;
import org.l2jmobius.gameserver.model.actor.Summon; import org.l2jmobius.gameserver.model.actor.Summon;
import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
import org.l2jmobius.gameserver.model.zone.ZoneType;
import org.l2jmobius.gameserver.network.serverpackets.CharInfo; import org.l2jmobius.gameserver.network.serverpackets.CharInfo;
import org.l2jmobius.gameserver.network.serverpackets.CreatureSay; import org.l2jmobius.gameserver.network.serverpackets.CreatureSay;
import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage; import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
@@ -191,4 +193,29 @@ public class Broadcast
{ {
toAllOnlinePlayers(new ExShowScreenMessage(text, 10000)); toAllOnlinePlayers(new ExShowScreenMessage(text, 10000));
} }
/**
* Send a packet to all players in a specific zone type.
* @param <T> ZoneType.
* @param zoneType : The zone type to send packets.
* @param packets : The packets to send.
*/
public static <T extends ZoneType> void toAllPlayersInZoneType(Class<T> zoneType, IClientOutgoingPacket... packets)
{
for (ZoneType zone : ZoneManager.getInstance().getAllZones(zoneType))
{
for (Creature creature : zone.getCharactersInside())
{
if (creature == null)
{
continue;
}
for (IClientOutgoingPacket packet : packets)
{
creature.sendPacket(packet);
}
}
}
}
} }

View File

@@ -0,0 +1,15 @@
CREATE TABLE IF NOT EXISTS `mdt_bets` (
`lane_id` INT(1) DEFAULT 0,
`bet` INT DEFAULT 0,
PRIMARY KEY (`lane_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;
INSERT INTO `mdt_bets` VALUES
('1','0'),
('2','0'),
('3','0'),
('4','0'),
('5','0'),
('6','0'),
('7','0'),
('8','0');

View File

@@ -0,0 +1,7 @@
CREATE TABLE IF NOT EXISTS `mdt_history` (
`race_id` MEDIUMINT DEFAULT 0,
`first` INT(1) DEFAULT 0,
`second` INT(1) DEFAULT 0,
`odd_rate` DOUBLE(10,2) DEFAULT 0,
PRIMARY KEY (`race_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;

View File

@@ -416,9 +416,6 @@
<admin command="admin_mobgroup_attackgrp" accessLevel="100" /> <admin command="admin_mobgroup_attackgrp" accessLevel="100" />
<admin command="admin_mobgroup_invul" accessLevel="100" /> <admin command="admin_mobgroup_invul" accessLevel="100" />
<!-- ADMIN MONSTER RACE -->
<admin command="admin_mons" accessLevel="100" />
<!-- ADMIN ONLINE --> <!-- ADMIN ONLINE -->
<admin command="admin_online" accessLevel="100" /> <admin command="admin_online" accessLevel="100" />

View File

@@ -1,19 +1,22 @@
<html><body><br><br> <html><body>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 0">Back</Button><br> <br>
In a monster race eight monsters race at the same time. If you guess which monster is expected to finish first and buy that monster's race ticket, based upon the odds to win you will receive an appropriate amount of Adena.<br> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 0">Go Back</Button>
<font color="LEVEL">How the Race Works</font>Monster races run every 20 minutes in real time.<br> <br>
<font color="LEVEL">How to Participate</font><br> In a monster race eight monsters race at the same time. If you guess which monster is expected to finish first and buy that monster's race ticket, based upon the odds to win you will receive an appropriate amount of adena.<br>
You can purchase a monster race ticket through a race manager NPC. Only between 18 minutes before the start of a race until 3 minutes before the race begins can you buy a ticket for the applicable race. When you purchase a ticket through a race manager, you can find out the names of the participating monsters and their current statistics. Three minutes before a race starts, the race manager will stop ticket sales and announce the monsters' individual odds for the current race.<br> <font color="LEVEL">Racing Schedule</font><br>
<font color="LEVEL">Betting</font><br> Monster races every 20 minutes in real time.<br>
There are two types of race betting. The Win bet means that you back a particular monster to win 1st place. The Place bet means that you will have to guess the 1st and 2nd place monsters, regardless of who finishes first or second.<br> <font color="LEVEL">Method of Race Participation</font><br>
You can purchase a monster race ticket through a race manager NPC. Only between 18 minutes before the start of a race until 3 minutes before the race begins you can buy a ticket for the applicable race. When you purchase a ticket through a conversation with a race manager, you can find out the names of participating monsters in the race and their current statistics. Five minutes before a race starts, the race manager will stop ticket sales and announce monsters' individual odds rate for the current race.<br>
<font color="LEVEL">Types of Race</font><br>
There are two types of race betting. The Win type betting means that you back a monster that will win the first prize. Place type of betting means that you will have to guess the 1st and 2nd winning monsters, regardless of who finishes first or second.<br>
<font color="00FFFF">Win</font><br1> <font color="00FFFF">Win</font><br1>
* Among the entire betting amount for Win betting, after subtracting the race track's own share of profit, the amount left will be your total winnings. Three minutes before the race, each monster's expected odds are announced. Each Win ticket's dividend amount is calculated by multiplying the odds by the price of a ticket. The odds are never less than 1.<br1> * Among the entire betting amount for Win betting, after subtracting the race track's own share of profit, the amount left will be your total winnings. Three minutes before the race each monster's expected odds are announced. Each Win ticket's dividend amount is calculated by multiplying the odds by the price of a ticket. The odds rate is never less than 1.<br1>
* The prize money does not get transferred to the next race.<br> * The prize money does not get transferred to the next race.<br>
<font color="00FFFF">Place</font><br1> <font color="00FFFF">Place</font><br1>
* Place betting is a way of betting when you correctly guess the two monsters who will finish first and second regardless of their order. The method of calculating the dividend amount is the same as Win betting.<br1> * Place betting is a way of betting when you correctly guess the two monsters who will finish first and second regardless of their order. The method of calculating dividend amount is the same as Win betting.<br>
* Currently place betting is not available.<br> * Currently place betting is not offered.<br>
<font color="LEVEL">End of Race</font><br> <font color="LEVEL">End of Race</font><br>
When a race is over, the race manager will shout the results of the race. You can trade your ticket for Adena by speaking to a race manager NPC. Check the Winning Information Data to find out the types of bets and total winnings for the race you bet on, as well as your win status and prize amount. If you won, you can receive your prize money thorough a race manager NPC. If you did not win, a race manager will buy back your ticket for a small amount of money.<br> When a race is over, the race manager will shout out the results of the race. Through a conversation with a race manager NPC, you can trade your ticket for adena. By looking up on the Winning Information Data, you can find out the types of tickets and quantity of the winnings in the race you betted on and your win status and prize amount you won. If you won, you can receive your prize money thorough a race manager NPC. If you did not win, a race manager will buy back your ticket by paying you a very small amount of money.<br>
If a server goes down while tickets are being sold, or while a race is in progress, the race will be cancelled. You can receive a full refund for your ticket for a cancelled race through a race manager.<br> If a server goes down while tickets are being sold, or while a race is in progress, the race will be cancelled. You can receive a full refund for your ticket for a cancelled race through a race manager.<br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 0">Back</Button> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 0">Go Back</Button>
</body></html> </body></html>

View File

@@ -3,6 +3,7 @@
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=1 height=3> <img src="L2UI.SquareBlank" width=1 height=3>
</center> </center>
<table width=270 border=0 bgcolor="111111"> <table width=270 border=0 bgcolor="111111">
<tr> <tr>
<td fixwidth=60>Lane Selection</td> <td fixwidth=60>Lane Selection</td>
@@ -11,6 +12,7 @@
<td fixwidth=30 align=right>Odds</td> <td fixwidth=30 align=right>Odds</td>
</tr> </tr>
</table> </table>
<table width=270 border=0> <table width=270 border=0>
<tr> <tr>
<td fixwidth=13><img src="L2UI.rightBtn2" width=14 height=13></td> <td fixwidth=13><img src="L2UI.rightBtn2" width=14 height=13></td>
@@ -20,18 +22,21 @@
<td fixwidth=30 align=right><font color="00ffff">13%</font></td> <td fixwidth=30 align=right><font color="00ffff">13%</font></td>
</tr> </tr>
</table> </table>
<table width=270 border=0> <table width=270 border=0>
<tr><td width=100>Select Bet Amount</td> <tr><td width=100>Select Bet Amount</td>
<td width=170> <td width=170>
<a action="bypass -h npc_%objectId%_BuyTicket 11" >100 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 12" >500 Adena</a><br1> <a action="bypass -h npc_%objectId%_BuyTicket 11" >100 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 12" >500 Adena</a> <br1>
<a action="bypass -h npc_%objectId%_BuyTicket 13" >1,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 14" >5,000 Adena</a><br1> <a action="bypass -h npc_%objectId%_BuyTicket 13" >1,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 14" >5,000 Adena</a> <br1>
<a action="bypass -h npc_%objectId%_BuyTicket 15" >10,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 16" >20,000 Adena</a><br1> <a action="bypass -h npc_%objectId%_BuyTicket 15" >10,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 16" >20,000 Adena</a> <br1>
<a action="bypass -h npc_%objectId%_BuyTicket 17" >50,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 18" >100,000 Adena</a><br1> <a action="bypass -h npc_%objectId%_BuyTicket 17" >50,000 Adena</a> , <a action="bypass -h npc_%objectId%_BuyTicket 18" >100,000 Adena</a> <br1>
</td></tr> </td></tr>
</table> </table>
<img src="L2UI.SquareBlank" width=270 height=10> <img src="L2UI.SquareBlank" width=270 height=10>
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=270 height=5> <img src="L2UI.SquareBlank" width=270 height=5>
<table width=270 border=0 cellpadding=0 cellspacing=0> <table width=270 border=0 cellpadding=0 cellspacing=0>
<tr> <tr>
<td width=70 height=20><button value="Select Lane" action="bypass -h npc_%objectId%_BuyTicket 0" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td> <td width=70 height=20><button value="Select Lane" action="bypass -h npc_%objectId%_BuyTicket 0" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td>
@@ -39,4 +44,5 @@
<td width=70 align=right><button value="Confirm" action="bypass -h npc_%objectId%_BuyTicket 20" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td> <td width=70 align=right><button value="Confirm" action="bypass -h npc_%objectId%_BuyTicket 20" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td>
</tr> </tr>
</table> </table>
</body></html> </body></html>

View File

@@ -11,6 +11,7 @@
<td fixwidth=30 align=right>Odds</td> <td fixwidth=30 align=right>Odds</td>
</tr> </tr>
</table> </table>
<table width=270 border=0> <table width=270 border=0>
<tr> <tr>
<td fixwidth=13><img src="L2UI.rightBtn2" width=14 height=13></td> <td fixwidth=13><img src="L2UI.rightBtn2" width=14 height=13></td>
@@ -20,6 +21,7 @@
<td fixwidth=30 align=right><font color="00ffff">13%</font></td> <td fixwidth=30 align=right><font color="00ffff">13%</font></td>
</tr> </tr>
</table> </table>
<table width=270 border=0 bgcolor=111111> <table width=270 border=0 bgcolor=111111>
<tr> <tr>
<td fixwidth=60>Bet Amount </td> <td fixwidth=60>Bet Amount </td>
@@ -37,11 +39,13 @@
<td>Total </td> <td>Total </td>
<td><font color="LEVEL">0total</font> Adena</td> <td><font color="LEVEL">0total</font> Adena</td>
</tr> </tr>
</table><br> </table>
<br>
<center>If the information is correct, please click Confirm.</center><br> <center>If the information is correct, please click Confirm.</center><br>
<img src="L2UI.SquareBlank" width=270 height=10> <img src="L2UI.SquareBlank" width=270 height=10>
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=270 height=5> <img src="L2UI.SquareBlank" width=270 height=5>
<table width=270 border=0 cellpadding=0 cellspacing=0> <table width=270 border=0 cellpadding=0 cellspacing=0>
<tr> <tr>
<td width=70 height=20><button value=&$141; action="bypass -h npc_%objectId%_Chat 0" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td> <td width=70 height=20><button value=&$141; action="bypass -h npc_%objectId%_Chat 0" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td>

View File

@@ -15,49 +15,49 @@
<tr> <tr>
<td fixwidth=40>No.1</td> <td fixwidth=40>No.1</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob1</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob1</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd1</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.2</td> <td fixwidth=40>No.2</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob2</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob2</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd2</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.3</td> <td fixwidth=40>No.3</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob3</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob3</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd3</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.4</td> <td fixwidth=40>No.4</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob4</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob4</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd4</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.5</td> <td fixwidth=40>No.5</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob5</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob5</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd5</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.6</td> <td fixwidth=40>No.6</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob6</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob6</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd6</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.7</td> <td fixwidth=40>No.7</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob7</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob7</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd7</font> </td>
</tr> </tr>
<tr> <tr>
<td fixwidth=40>No.8</td> <td fixwidth=40>No.8</td>
<td fixwidth=130 align=left><font color="LEVEL">Mob8</font></td> <td fixwidth=130 align=left><font color="LEVEL">Mob8</font></td>
<td fixwidth=100 align=right><font color="00ffff">&$804;</font> </td> <td fixwidth=100 align=right><font color="00ffff">Odd8</font> </td>
</tr> </tr>
</table> </table>
<img src="L2UI.SquareBlank" width=270 height=10> <img src="L2UI.SquareBlank" width=270 height=10>

View File

@@ -3,22 +3,12 @@
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=1 height=3> <img src="L2UI.SquareBlank" width=1 height=3>
<table width=270 border=0 bgcolor=111111> <table width=270 border=0 bgcolor=111111>
<tr><td fixwidth=60>Winnings for a Race.</td><td fixwidth=60 align=right>Lane</td><td fixwidth=160 align=right>Bet Amount</td></tr> <tr><td fixwidth=70>Winnings for a Race.</td><td fixwidth=70 align=right>Lane</td><td fixwidth=130 align=right>Bet Amount</td></tr>
<tr><td fixwidth=60 fixheight=1></td><td fixwidth=60></td><td fixwidth=160></td></tr> <tr><td fixwidth=70 fixheight=1></td><td fixwidth=70></td><td fixwidth=130></td></tr>
</table> </table>
<table width=270 border=0> <table width=270 border=0>
<tr> %tickets%
<td><Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Chat 8">1 Race Number</Button></td>
<td align=right><font color="LEVEL">1</font> Number</td>
<td align=right><font color="LEVEL">100</font> Adena</td>
</tr>
</table> </table>
<img src="L2UI.SquareWhite" width=270 height=1>
<table width=270 border=0>
<tr><td width=90><Button ALIGN=LEFT ICON="NORMAL" action="bypass _mrsl01_1">Previous List</Button></td><td width=90 align=center>1 Page </td><td width=90 align=right><Button ALIGN=LEFT ICON="NORMAL" action="bypass _mrsl01_2"> Next List</Button></td></tr>
</table>
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>
<img src="L2UI.SquareBlank" width=270 height=5> <img src="L2UI.SquareBlank" width=270 height=5>
<table width=270 border=0 cellpadding=0 cellspacing=0> <table width=270 border=0 cellpadding=0 cellspacing=0>

View File

@@ -4,11 +4,11 @@
<img src="L2UI.SquareBlank" width=1 height=3> <img src="L2UI.SquareBlank" width=1 height=3>
<table width=270 border=0> <table width=270 border=0>
<tr> <tr>
<td width=230>Race #19090<br> <td width=230>Race #%raceId%<br>
Lane 1<br> Lane: %lane%<br>
Bet: 100 adena<br> Bet: %bet% Adena<br>
1st place: Lane [4]<br> 1st place: Lane [%firstLane%]<br>
Odds:[0.01] Odds: [%odd%]
</td> </td>
</tr> </tr>
</table> </table>
@@ -18,8 +18,8 @@ Odds:[0.01]
<table width=270 border=0 cellpadding=0 cellspacing=0> <table width=270 border=0 cellpadding=0 cellspacing=0>
<tr> <tr>
<td width=90 height=20><button value="Previous" action="bypass -h npc_%objectId%_Chat 7" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td> <td width=90 height=20><button value="Previous" action="bypass -h npc_%objectId%_ShowTickets" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td>
<td width=90></td> <td width=90></td>
<td width=90 align=right><button value="Calculate" action="bypass _mrsl03_30635184" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td> <td width=90 align=right><button value="Calculate" action="bypass -h npc_%objectId%_CalculateWin %ticketObjectId%" back="L2UI.DefaultButton_click" fore="L2UI.DefaultButton" width=67 height=19></td>
</tr></table> </tr></table>
</body></html> </body></html>

View File

@@ -7,41 +7,7 @@
</table> </table>
<table width=270 border=0> <table width=270 border=0>
<tr><td fixwidth=60 fixheight=1></td><td fixwidth=70></td><td fixwidth=70></td><td fixwidth=70></td></tr> <tr><td fixwidth=60 fixheight=1></td><td fixwidth=70></td><td fixwidth=70></td><td fixwidth=70></td></tr>
<tr> %infos%
<td><font color="LEVEL">7</font> th</td>
<td><font color="LEVEL">8</font> Lane </td><td><font color="LEVEL">1</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">6</font> th</td>
<td><font color="LEVEL">4</font> Lane </td><td><font color="LEVEL">8</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">5</font> th</td>
<td><font color="LEVEL">7</font> Lane </td><td><font color="LEVEL">2</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">4</font> th</td>
<td><font color="LEVEL">6</font> Lane </td><td><font color="LEVEL">8</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">3</font> th</td>
<td><font color="LEVEL">4</font> Lane </td><td><font color="LEVEL">1</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">2</font> th</td>
<td><font color="LEVEL">1</font> Lane </td><td><font color="LEVEL">8</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
<tr>
<td><font color="LEVEL">1</font> th</td>
<td><font color="LEVEL">7</font> Lane </td><td><font color="LEVEL">6</font> Lane</td>
<td align=right><font color="00ffff">0.00</font> Times</td>
</tr>
</table> </table>
<img src="L2UI.SquareBlank" width=270 height=10> <img src="L2UI.SquareBlank" width=270 height=10>
<img src="L2UI.SquareWhite" width=270 height=1> <img src="L2UI.SquareWhite" width=270 height=1>

Some files were not shown because too many files have changed in this diff Show More